FontRenderer.cpp revision b0317984d34da99b614597ad0a8b39268eacb783
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 2 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 < mMaxWidth) { 54 *retOriginX = mCurrentCol + 1; 55 *retOriginY = mCurrentRow + 1; 56 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE; 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) { 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, NULL); 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 = (float) startX / (float) cacheWidth; 416 glyph->mBitmapMinV = (float) startY / (float) cacheHeight; 417 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; 418 glyph->mBitmapMaxV = (float) 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 glDeleteBuffers(1, &mIndexBufferID); 522 523 delete[] mTextMeshPtr; 524 delete mCacheTextureSmall; 525 delete mCacheTexture128; 526 delete mCacheTexture256; 527 delete mCacheTexture512; 528 } 529 530 Vector<Font*> fontsToDereference = mActiveFonts; 531 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 532 delete fontsToDereference[i]; 533 } 534} 535 536void FontRenderer::flushAllAndInvalidate() { 537 if (mCurrentQuadIndex != 0) { 538 issueDrawCommand(); 539 mCurrentQuadIndex = 0; 540 } 541 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 542 mActiveFonts[i]->invalidateTextureCache(); 543 } 544 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 545 mCacheLines[i]->mCurrentCol = 0; 546 } 547} 548 549void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) { 550 if (cacheTexture && cacheTexture->mTexture) { 551 glDeleteTextures(1, &cacheTexture->mTextureId); 552 delete cacheTexture->mTexture; 553 cacheTexture->mTexture = NULL; 554 } 555} 556 557void FontRenderer::flushLargeCaches() { 558 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) && 559 (!mCacheTexture256 || !mCacheTexture256->mTexture) && 560 (!mCacheTexture512 || !mCacheTexture512->mTexture)) { 561 // Typical case; no large glyph caches allocated 562 return; 563 } 564 565 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 566 CacheTextureLine* cacheLine = mCacheLines[i]; 567 if ((cacheLine->mCacheTexture == mCacheTexture128 || 568 cacheLine->mCacheTexture == mCacheTexture256 || 569 cacheLine->mCacheTexture == mCacheTexture512) && 570 cacheLine->mCacheTexture->mTexture != NULL) { 571 cacheLine->mCurrentCol = 0; 572 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 573 mActiveFonts[i]->invalidateTextureCache(cacheLine); 574 } 575 } 576 } 577 578 deallocateTextureMemory(mCacheTexture128); 579 deallocateTextureMemory(mCacheTexture256); 580 deallocateTextureMemory(mCacheTexture512); 581} 582 583void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) { 584 int width = cacheTexture->mWidth; 585 int height = cacheTexture->mHeight; 586 cacheTexture->mTexture = new uint8_t[width * height]; 587 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t)); 588 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 589 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 590 // Initialize texture dimensions 591 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, 592 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 593 594 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST; 595 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 596 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 597 598 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 599 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 600} 601 602void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 603 uint32_t* retOriginX, uint32_t* retOriginY) { 604 cachedGlyph->mIsValid = false; 605 // If the glyph is too tall, don't cache it 606 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 607 ALOGE("Font size to large to fit in cache. width, height = %i, %i", 608 (int) glyph.fWidth, (int) glyph.fHeight); 609 return; 610 } 611 612 // Now copy the bitmap into the cache texture 613 uint32_t startX = 0; 614 uint32_t startY = 0; 615 616 bool bitmapFit = false; 617 CacheTextureLine *cacheLine; 618 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 619 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 620 if (bitmapFit) { 621 cacheLine = mCacheLines[i]; 622 break; 623 } 624 } 625 626 // If the new glyph didn't fit, flush the state so far and invalidate everything 627 if (!bitmapFit) { 628 flushAllAndInvalidate(); 629 630 // Try to fit it again 631 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 632 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 633 if (bitmapFit) { 634 cacheLine = mCacheLines[i]; 635 break; 636 } 637 } 638 639 // if we still don't fit, something is wrong and we shouldn't draw 640 if (!bitmapFit) { 641 return; 642 } 643 } 644 645 cachedGlyph->mCachedTextureLine = cacheLine; 646 647 *retOriginX = startX; 648 *retOriginY = startY; 649 650 uint32_t endX = startX + glyph.fWidth; 651 uint32_t endY = startY + glyph.fHeight; 652 653 uint32_t cacheWidth = cacheLine->mMaxWidth; 654 655 CacheTexture *cacheTexture = cacheLine->mCacheTexture; 656 if (cacheTexture->mTexture == NULL) { 657 // Large-glyph texture memory is allocated only as needed 658 allocateTextureMemory(cacheTexture); 659 } 660 uint8_t* cacheBuffer = cacheTexture->mTexture; 661 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 662 unsigned int stride = glyph.rowBytes(); 663 664 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 665 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 666 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 667 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 668 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 669 } 670 } 671 672 cachedGlyph->mIsValid = true; 673} 674 675CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 676 GLuint textureId; 677 glGenTextures(1, &textureId); 678 uint8_t* textureMemory = NULL; 679 680 CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height); 681 if (allocate) { 682 allocateTextureMemory(cacheTexture); 683 } 684 return cacheTexture; 685} 686 687void FontRenderer::initTextTexture() { 688 mCacheLines.clear(); 689 690 // Next, use other, separate caches for large glyphs. 691 uint16_t maxWidth = 0; 692 if (Caches::hasInstance()) { 693 maxWidth = Caches::getInstance().maxTextureSize; 694 } 695 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) { 696 maxWidth = MAX_TEXT_CACHE_WIDTH; 697 } 698 if (mCacheTextureSmall != NULL) { 699 delete mCacheTextureSmall; 700 delete mCacheTexture128; 701 delete mCacheTexture256; 702 delete mCacheTexture512; 703 } 704 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true); 705 mCacheTexture128 = createCacheTexture(maxWidth, 256, false); 706 mCacheTexture256 = createCacheTexture(maxWidth, 256, false); 707 mCacheTexture512 = createCacheTexture(maxWidth, 512, false); 708 mCurrentCacheTexture = mCacheTextureSmall; 709 710 mUploadTexture = false; 711 // Split up our default cache texture into lines of certain widths 712 int nextLine = 0; 713 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall)); 714 nextLine += mCacheLines.top()->mMaxHeight; 715 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 716 nextLine += mCacheLines.top()->mMaxHeight; 717 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 718 nextLine += mCacheLines.top()->mMaxHeight; 719 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 720 nextLine += mCacheLines.top()->mMaxHeight; 721 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 722 nextLine += mCacheLines.top()->mMaxHeight; 723 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall)); 724 nextLine += mCacheLines.top()->mMaxHeight; 725 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine, 726 nextLine, 0, mCacheTextureSmall)); 727 728 // The first cache is split into 2 lines of height 128, the rest have just one cache line. 729 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128)); 730 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128)); 731 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256)); 732 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512)); 733} 734 735// Avoid having to reallocate memory and render quad by quad 736void FontRenderer::initVertexArrayBuffers() { 737 uint32_t numIndices = mMaxNumberOfQuads * 6; 738 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 739 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 740 741 // Four verts, two triangles , six indices per quad 742 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 743 int i6 = i * 6; 744 int i4 = i * 4; 745 746 indexBufferData[i6 + 0] = i4 + 0; 747 indexBufferData[i6 + 1] = i4 + 1; 748 indexBufferData[i6 + 2] = i4 + 2; 749 750 indexBufferData[i6 + 3] = i4 + 0; 751 indexBufferData[i6 + 4] = i4 + 2; 752 indexBufferData[i6 + 5] = i4 + 3; 753 } 754 755 glGenBuffers(1, &mIndexBufferID); 756 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 757 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 758 759 free(indexBufferData); 760 761 uint32_t coordSize = 2; 762 uint32_t uvSize = 2; 763 uint32_t vertsPerQuad = 4; 764 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 765 mTextMeshPtr = new float[vertexBufferSize]; 766} 767 768// We don't want to allocate anything unless we actually draw text 769void FontRenderer::checkInit() { 770 if (mInitialized) { 771 return; 772 } 773 774 initTextTexture(); 775 initVertexArrayBuffers(); 776 777 // We store a string with letters in a rough frequency of occurrence 778 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); 779 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); 780 mLatinPrecache += String16(",.?!()-+@;:`'"); 781 mLatinPrecache += String16("0123456789"); 782 783 mInitialized = true; 784} 785 786void FontRenderer::checkTextureUpdate() { 787 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) { 788 return; 789 } 790 791 Caches& caches = Caches::getInstance(); 792 GLuint lastTextureId = 0; 793 // Iterate over all the cache lines and see which ones need to be updated 794 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 795 CacheTextureLine* cl = mCacheLines[i]; 796 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) { 797 CacheTexture* cacheTexture = cl->mCacheTexture; 798 uint32_t xOffset = 0; 799 uint32_t yOffset = cl->mCurrentRow; 800 uint32_t width = cl->mMaxWidth; 801 uint32_t height = cl->mMaxHeight; 802 void* textureData = cacheTexture->mTexture + (yOffset * width); 803 804 if (cacheTexture->mTextureId != lastTextureId) { 805 caches.activeTexture(0); 806 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 807 lastTextureId = cacheTexture->mTextureId; 808 } 809 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 810 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 811 812 cl->mDirty = false; 813 } 814 } 815 816 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); 817 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) { 818 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST; 819 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 820 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 821 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering; 822 } 823 mLastCacheTexture = mCurrentCacheTexture; 824 825 mUploadTexture = false; 826} 827 828void FontRenderer::issueDrawCommand() { 829 checkTextureUpdate(); 830 831 Caches& caches = Caches::getInstance(); 832 caches.bindIndicesBuffer(mIndexBufferID); 833 if (!mDrawn) { 834 float* buffer = mTextMeshPtr; 835 int offset = 2; 836 837 bool force = caches.unbindMeshBuffer(); 838 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 839 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 840 buffer + offset); 841 } 842 843 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 844 845 mDrawn = true; 846} 847 848void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 849 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 850 float x4, float y4, float u4, float v4, CacheTexture* texture) { 851 if (texture != mCurrentCacheTexture) { 852 if (mCurrentQuadIndex != 0) { 853 // First, draw everything stored already which uses the previous texture 854 issueDrawCommand(); 855 mCurrentQuadIndex = 0; 856 } 857 // Now use the new texture id 858 mCurrentCacheTexture = texture; 859 } 860 861 const uint32_t vertsPerQuad = 4; 862 const uint32_t floatsPerVert = 4; 863 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 864 865 (*currentPos++) = x1; 866 (*currentPos++) = y1; 867 (*currentPos++) = u1; 868 (*currentPos++) = v1; 869 870 (*currentPos++) = x2; 871 (*currentPos++) = y2; 872 (*currentPos++) = u2; 873 (*currentPos++) = v2; 874 875 (*currentPos++) = x3; 876 (*currentPos++) = y3; 877 (*currentPos++) = u3; 878 (*currentPos++) = v3; 879 880 (*currentPos++) = x4; 881 (*currentPos++) = y4; 882 (*currentPos++) = u4; 883 (*currentPos++) = v4; 884 885 mCurrentQuadIndex++; 886} 887 888void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 889 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 890 float x4, float y4, float u4, float v4, CacheTexture* texture) { 891 892 if (mClip && 893 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 894 return; 895 } 896 897 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 898 899 if (mBounds) { 900 mBounds->left = fmin(mBounds->left, x1); 901 mBounds->top = fmin(mBounds->top, y3); 902 mBounds->right = fmax(mBounds->right, x3); 903 mBounds->bottom = fmax(mBounds->bottom, y1); 904 } 905 906 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 907 issueDrawCommand(); 908 mCurrentQuadIndex = 0; 909 } 910} 911 912void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 913 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 914 float x4, float y4, float u4, float v4, CacheTexture* texture) { 915 916 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 917 918 if (mBounds) { 919 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 920 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 921 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 922 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 923 } 924 925 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 926 issueDrawCommand(); 927 mCurrentQuadIndex = 0; 928 } 929} 930 931uint32_t FontRenderer::getRemainingCacheCapacity() { 932 uint32_t remainingCapacity = 0; 933 float totalPixels = 0; 934 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 935 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 936 totalPixels += mCacheLines[i]->mMaxWidth; 937 } 938 remainingCapacity = (remainingCapacity * 100) / totalPixels; 939 return remainingCapacity; 940} 941 942void FontRenderer::precacheLatin(SkPaint* paint) { 943 // Remaining capacity is measured in % 944 uint32_t remainingCapacity = getRemainingCacheCapacity(); 945 uint32_t precacheIdx = 0; 946 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 947 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]); 948 remainingCapacity = getRemainingCacheCapacity(); 949 precacheIdx ++; 950 } 951} 952 953void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 954 uint32_t currentNumFonts = mActiveFonts.size(); 955 int flags = 0; 956 if (paint->isFakeBoldText()) { 957 flags |= Font::kFakeBold; 958 } 959 960 const float skewX = paint->getTextSkewX(); 961 uint32_t italicStyle = *(uint32_t*) &skewX; 962 const float scaleXFloat = paint->getTextScaleX(); 963 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 964 SkPaint::Style style = paint->getStyle(); 965 const float strokeWidthFloat = paint->getStrokeWidth(); 966 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 967 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 968 scaleX, style, strokeWidth); 969 970 const float maxPrecacheFontSize = 40.0f; 971 bool isNewFont = currentNumFonts != mActiveFonts.size(); 972 973 if (isNewFont && fontSize <= maxPrecacheFontSize) { 974 precacheLatin(paint); 975 } 976} 977 978FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 979 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { 980 checkInit(); 981 982 if (!mCurrentFont) { 983 DropShadow image; 984 image.width = 0; 985 image.height = 0; 986 image.image = NULL; 987 image.penX = 0; 988 image.penY = 0; 989 return image; 990 } 991 992 mDrawn = false; 993 mClip = NULL; 994 mBounds = NULL; 995 996 Rect bounds; 997 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds); 998 999 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 1000 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 1001 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 1002 1003 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 1004 dataBuffer[i] = 0; 1005 } 1006 1007 int penX = radius - bounds.left; 1008 int penY = radius - bounds.bottom; 1009 1010 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 1011 dataBuffer, paddedWidth, paddedHeight); 1012 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 1013 1014 DropShadow image; 1015 image.width = paddedWidth; 1016 image.height = paddedHeight; 1017 image.image = dataBuffer; 1018 image.penX = penX; 1019 image.penY = penY; 1020 1021 return image; 1022} 1023 1024void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 1025 checkInit(); 1026 1027 mDrawn = false; 1028 mBounds = bounds; 1029 mClip = clip; 1030} 1031 1032void FontRenderer::finishRender() { 1033 mBounds = NULL; 1034 mClip = NULL; 1035 1036 if (mCurrentQuadIndex != 0) { 1037 issueDrawCommand(); 1038 mCurrentQuadIndex = 0; 1039 } 1040} 1041 1042bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 1043 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 1044 if (!mCurrentFont) { 1045 ALOGE("No font set"); 1046 return false; 1047 } 1048 1049 initRender(clip, bounds); 1050 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 1051 finishRender(); 1052 1053 return mDrawn; 1054} 1055 1056bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 1057 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 1058 const float* positions, Rect* bounds) { 1059 if (!mCurrentFont) { 1060 ALOGE("No font set"); 1061 return false; 1062 } 1063 1064 initRender(clip, bounds); 1065 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 1066 finishRender(); 1067 1068 return mDrawn; 1069} 1070 1071bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 1072 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 1073 float hOffset, float vOffset, Rect* bounds) { 1074 if (!mCurrentFont) { 1075 ALOGE("No font set"); 1076 return false; 1077 } 1078 1079 initRender(clip, bounds); 1080 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 1081 finishRender(); 1082 1083 return mDrawn; 1084} 1085 1086void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 1087 // Compute gaussian weights for the blur 1088 // e is the euler's number 1089 float e = 2.718281828459045f; 1090 float pi = 3.1415926535897932f; 1091 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 1092 // x is of the form [-radius .. 0 .. radius] 1093 // and sigma varies with radius. 1094 // Based on some experimental radius values and sigma's 1095 // we approximately fit sigma = f(radius) as 1096 // sigma = radius * 0.3 + 0.6 1097 // The larger the radius gets, the more our gaussian blur 1098 // will resemble a box blur since with large sigma 1099 // the gaussian curve begins to lose its shape 1100 float sigma = 0.3f * (float) radius + 0.6f; 1101 1102 // Now compute the coefficints 1103 // We will store some redundant values to save some math during 1104 // the blur calculations 1105 // precompute some values 1106 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 1107 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 1108 1109 float normalizeFactor = 0.0f; 1110 for (int32_t r = -radius; r <= radius; r ++) { 1111 float floatR = (float) r; 1112 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 1113 normalizeFactor += weights[r + radius]; 1114 } 1115 1116 //Now we need to normalize the weights because all our coefficients need to add up to one 1117 normalizeFactor = 1.0f / normalizeFactor; 1118 for (int32_t r = -radius; r <= radius; r ++) { 1119 weights[r + radius] *= normalizeFactor; 1120 } 1121} 1122 1123void FontRenderer::horizontalBlur(float* weights, int32_t radius, 1124 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1125 float blurredPixel = 0.0f; 1126 float currentPixel = 0.0f; 1127 1128 for (int32_t y = 0; y < height; y ++) { 1129 1130 const uint8_t* input = source + y * width; 1131 uint8_t* output = dest + y * width; 1132 1133 for (int32_t x = 0; x < width; x ++) { 1134 blurredPixel = 0.0f; 1135 const float* gPtr = weights; 1136 // Optimization for non-border pixels 1137 if (x > radius && x < (width - radius)) { 1138 const uint8_t *i = input + (x - radius); 1139 for (int r = -radius; r <= radius; r ++) { 1140 currentPixel = (float) (*i); 1141 blurredPixel += currentPixel * gPtr[0]; 1142 gPtr++; 1143 i++; 1144 } 1145 } else { 1146 for (int32_t r = -radius; r <= radius; r ++) { 1147 // Stepping left and right away from the pixel 1148 int validW = x + r; 1149 if (validW < 0) { 1150 validW = 0; 1151 } 1152 if (validW > width - 1) { 1153 validW = width - 1; 1154 } 1155 1156 currentPixel = (float) input[validW]; 1157 blurredPixel += currentPixel * gPtr[0]; 1158 gPtr++; 1159 } 1160 } 1161 *output = (uint8_t)blurredPixel; 1162 output ++; 1163 } 1164 } 1165} 1166 1167void FontRenderer::verticalBlur(float* weights, int32_t radius, 1168 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1169 float blurredPixel = 0.0f; 1170 float currentPixel = 0.0f; 1171 1172 for (int32_t y = 0; y < height; y ++) { 1173 1174 uint8_t* output = dest + y * width; 1175 1176 for (int32_t x = 0; x < width; x ++) { 1177 blurredPixel = 0.0f; 1178 const float* gPtr = weights; 1179 const uint8_t* input = source + x; 1180 // Optimization for non-border pixels 1181 if (y > radius && y < (height - radius)) { 1182 const uint8_t *i = input + ((y - radius) * width); 1183 for (int32_t r = -radius; r <= radius; r ++) { 1184 currentPixel = (float)(*i); 1185 blurredPixel += currentPixel * gPtr[0]; 1186 gPtr++; 1187 i += width; 1188 } 1189 } else { 1190 for (int32_t r = -radius; r <= radius; r ++) { 1191 int validH = y + r; 1192 // Clamp to zero and width 1193 if (validH < 0) { 1194 validH = 0; 1195 } 1196 if (validH > height - 1) { 1197 validH = height - 1; 1198 } 1199 1200 const uint8_t *i = input + validH * width; 1201 currentPixel = (float) (*i); 1202 blurredPixel += currentPixel * gPtr[0]; 1203 gPtr++; 1204 } 1205 } 1206 *output = (uint8_t) blurredPixel; 1207 output ++; 1208 } 1209 } 1210} 1211 1212 1213void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 1214 float *gaussian = new float[2 * radius + 1]; 1215 computeGaussianWeights(gaussian, radius); 1216 1217 uint8_t* scratch = new uint8_t[width * height]; 1218 1219 horizontalBlur(gaussian, radius, image, scratch, width, height); 1220 verticalBlur(gaussian, radius, scratch, image, width, height); 1221 1222 delete[] gaussian; 1223 delete[] scratch; 1224} 1225 1226}; // namespace uirenderer 1227}; // namespace android 1228