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