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