FontRenderer.cpp revision 33fa1f774c8e7289fd7c39fbc2c65b9361f2c2c4
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 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 967 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 968 totalPixels += mCacheLines[i]->mMaxWidth; 969 } 970 remainingCapacity = (remainingCapacity * 100) / totalPixels; 971 return remainingCapacity; 972} 973 974void FontRenderer::precacheLatin(SkPaint* paint) { 975 // Remaining capacity is measured in % 976 uint32_t remainingCapacity = getRemainingCacheCapacity(); 977 uint32_t precacheIndex = 0; 978 979 // We store a string with letters in a rough frequency of occurrence 980 String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789"); 981 982 size_t size = l.size(); 983 uint16_t latin[size]; 984 paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin); 985 986 while (remainingCapacity > 25 && precacheIndex < size) { 987 mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex])); 988 remainingCapacity = getRemainingCacheCapacity(); 989 precacheIndex++; 990 } 991} 992 993void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 994 uint32_t currentNumFonts = mActiveFonts.size(); 995 int flags = 0; 996 if (paint->isFakeBoldText()) { 997 flags |= Font::kFakeBold; 998 } 999 1000 const float skewX = paint->getTextSkewX(); 1001 uint32_t italicStyle = *(uint32_t*) &skewX; 1002 const float scaleXFloat = paint->getTextScaleX(); 1003 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 1004 SkPaint::Style style = paint->getStyle(); 1005 const float strokeWidthFloat = paint->getStrokeWidth(); 1006 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 1007 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 1008 scaleX, style, strokeWidth); 1009 1010 const float maxPrecacheFontSize = 40.0f; 1011 bool isNewFont = currentNumFonts != mActiveFonts.size(); 1012 1013 if (isNewFont && fontSize <= maxPrecacheFontSize) { 1014 precacheLatin(paint); 1015 } 1016} 1017 1018FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 1019 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 1020 checkInit(); 1021 1022 if (!mCurrentFont) { 1023 DropShadow image; 1024 image.width = 0; 1025 image.height = 0; 1026 image.image = NULL; 1027 image.penX = 0; 1028 image.penY = 0; 1029 return image; 1030 } 1031 1032 mDrawn = false; 1033 mClip = NULL; 1034 mBounds = NULL; 1035 1036 Rect bounds; 1037 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 1038 1039 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 1040 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 1041 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 1042 1043 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 1044 dataBuffer[i] = 0; 1045 } 1046 1047 int penX = radius - bounds.left; 1048 int penY = radius - bounds.bottom; 1049 1050 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 1051 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 1052 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 1053 1054 DropShadow image; 1055 image.width = paddedWidth; 1056 image.height = paddedHeight; 1057 image.image = dataBuffer; 1058 image.penX = penX; 1059 image.penY = penY; 1060 1061 return image; 1062} 1063 1064void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 1065 checkInit(); 1066 1067 mDrawn = false; 1068 mBounds = bounds; 1069 mClip = clip; 1070} 1071 1072void FontRenderer::finishRender() { 1073 mBounds = NULL; 1074 mClip = NULL; 1075 1076 if (mCurrentQuadIndex != 0) { 1077 issueDrawCommand(); 1078 mCurrentQuadIndex = 0; 1079 } 1080} 1081 1082bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 1083 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 1084 if (!mCurrentFont) { 1085 ALOGE("No font set"); 1086 return false; 1087 } 1088 1089 initRender(clip, bounds); 1090 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 1091 finishRender(); 1092 1093 return mDrawn; 1094} 1095 1096bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 1097 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 1098 const float* positions, Rect* bounds) { 1099 if (!mCurrentFont) { 1100 ALOGE("No font set"); 1101 return false; 1102 } 1103 1104 initRender(clip, bounds); 1105 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 1106 finishRender(); 1107 1108 return mDrawn; 1109} 1110 1111bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 1112 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 1113 float hOffset, float vOffset, Rect* bounds) { 1114 if (!mCurrentFont) { 1115 ALOGE("No font set"); 1116 return false; 1117 } 1118 1119 initRender(clip, bounds); 1120 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 1121 finishRender(); 1122 1123 return mDrawn; 1124} 1125 1126void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 1127 // Compute gaussian weights for the blur 1128 // e is the euler's number 1129 float e = 2.718281828459045f; 1130 float pi = 3.1415926535897932f; 1131 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 1132 // x is of the form [-radius .. 0 .. radius] 1133 // and sigma varies with radius. 1134 // Based on some experimental radius values and sigma's 1135 // we approximately fit sigma = f(radius) as 1136 // sigma = radius * 0.3 + 0.6 1137 // The larger the radius gets, the more our gaussian blur 1138 // will resemble a box blur since with large sigma 1139 // the gaussian curve begins to lose its shape 1140 float sigma = 0.3f * (float) radius + 0.6f; 1141 1142 // Now compute the coefficints 1143 // We will store some redundant values to save some math during 1144 // the blur calculations 1145 // precompute some values 1146 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 1147 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 1148 1149 float normalizeFactor = 0.0f; 1150 for (int32_t r = -radius; r <= radius; r ++) { 1151 float floatR = (float) r; 1152 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 1153 normalizeFactor += weights[r + radius]; 1154 } 1155 1156 //Now we need to normalize the weights because all our coefficients need to add up to one 1157 normalizeFactor = 1.0f / normalizeFactor; 1158 for (int32_t r = -radius; r <= radius; r ++) { 1159 weights[r + radius] *= normalizeFactor; 1160 } 1161} 1162 1163void FontRenderer::horizontalBlur(float* weights, int32_t radius, 1164 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1165 float blurredPixel = 0.0f; 1166 float currentPixel = 0.0f; 1167 1168 for (int32_t y = 0; y < height; y ++) { 1169 1170 const uint8_t* input = source + y * width; 1171 uint8_t* output = dest + y * width; 1172 1173 for (int32_t x = 0; x < width; x ++) { 1174 blurredPixel = 0.0f; 1175 const float* gPtr = weights; 1176 // Optimization for non-border pixels 1177 if (x > radius && x < (width - radius)) { 1178 const uint8_t *i = input + (x - radius); 1179 for (int r = -radius; r <= radius; r ++) { 1180 currentPixel = (float) (*i); 1181 blurredPixel += currentPixel * gPtr[0]; 1182 gPtr++; 1183 i++; 1184 } 1185 } else { 1186 for (int32_t r = -radius; r <= radius; r ++) { 1187 // Stepping left and right away from the pixel 1188 int validW = x + r; 1189 if (validW < 0) { 1190 validW = 0; 1191 } 1192 if (validW > width - 1) { 1193 validW = width - 1; 1194 } 1195 1196 currentPixel = (float) input[validW]; 1197 blurredPixel += currentPixel * gPtr[0]; 1198 gPtr++; 1199 } 1200 } 1201 *output = (uint8_t)blurredPixel; 1202 output ++; 1203 } 1204 } 1205} 1206 1207void FontRenderer::verticalBlur(float* weights, int32_t radius, 1208 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1209 float blurredPixel = 0.0f; 1210 float currentPixel = 0.0f; 1211 1212 for (int32_t y = 0; y < height; y ++) { 1213 1214 uint8_t* output = dest + y * width; 1215 1216 for (int32_t x = 0; x < width; x ++) { 1217 blurredPixel = 0.0f; 1218 const float* gPtr = weights; 1219 const uint8_t* input = source + x; 1220 // Optimization for non-border pixels 1221 if (y > radius && y < (height - radius)) { 1222 const uint8_t *i = input + ((y - radius) * width); 1223 for (int32_t r = -radius; r <= radius; r ++) { 1224 currentPixel = (float)(*i); 1225 blurredPixel += currentPixel * gPtr[0]; 1226 gPtr++; 1227 i += width; 1228 } 1229 } else { 1230 for (int32_t r = -radius; r <= radius; r ++) { 1231 int validH = y + r; 1232 // Clamp to zero and width 1233 if (validH < 0) { 1234 validH = 0; 1235 } 1236 if (validH > height - 1) { 1237 validH = height - 1; 1238 } 1239 1240 const uint8_t *i = input + validH * width; 1241 currentPixel = (float) (*i); 1242 blurredPixel += currentPixel * gPtr[0]; 1243 gPtr++; 1244 } 1245 } 1246 *output = (uint8_t) blurredPixel; 1247 output ++; 1248 } 1249 } 1250} 1251 1252 1253void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 1254 float *gaussian = new float[2 * radius + 1]; 1255 computeGaussianWeights(gaussian, radius); 1256 1257 uint8_t* scratch = new uint8_t[width * height]; 1258 1259 horizontalBlur(gaussian, radius, image, scratch, width, height); 1260 verticalBlur(gaussian, radius, scratch, image, width, height); 1261 1262 delete[] gaussian; 1263 delete[] scratch; 1264} 1265 1266}; // namespace uirenderer 1267}; // namespace android 1268