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