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