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