FontRenderer.cpp revision b1d0a4ed21168fefcb82232c8f22cb95d60acb85
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "OpenGLRenderer" 18 19#include <SkUtils.h> 20 21#include <cutils/properties.h> 22 23#include <utils/Log.h> 24 25#include "Caches.h" 26#include "Debug.h" 27#include "FontRenderer.h" 28#include "Caches.h" 29 30namespace android { 31namespace uirenderer { 32 33/////////////////////////////////////////////////////////////////////////////// 34// Defines 35/////////////////////////////////////////////////////////////////////////////// 36 37#define DEFAULT_TEXT_CACHE_WIDTH 1024 38#define DEFAULT_TEXT_CACHE_HEIGHT 256 39#define MAX_TEXT_CACHE_WIDTH 2048 40#define TEXTURE_BORDER_SIZE 2 41 42#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16) 43 44/////////////////////////////////////////////////////////////////////////////// 45// CacheTextureLine 46/////////////////////////////////////////////////////////////////////////////// 47 48bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { 49 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) { 50 return false; 51 } 52 53 if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) { 54 *retOriginX = mCurrentCol + 1; 55 *retOriginY = mCurrentRow + 1; 56 mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE; 57 mDirty = true; 58 return true; 59 } 60 61 return false; 62} 63 64/////////////////////////////////////////////////////////////////////////////// 65// Font 66/////////////////////////////////////////////////////////////////////////////// 67 68Font::Font(FontRenderer* state, uint32_t fontId, float fontSize, 69 int flags, uint32_t italicStyle, uint32_t scaleX, 70 SkPaint::Style style, uint32_t strokeWidth) : 71 mState(state), mFontId(fontId), mFontSize(fontSize), 72 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX), 73 mStyle(style), mStrokeWidth(mStrokeWidth) { 74} 75 76 77Font::~Font() { 78 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { 79 if (mState->mActiveFonts[ct] == this) { 80 mState->mActiveFonts.removeAt(ct); 81 break; 82 } 83 } 84 85 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 86 delete mCachedGlyphs.valueAt(i); 87 } 88} 89 90void Font::invalidateTextureCache(CacheTextureLine *cacheLine) { 91 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 92 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i); 93 if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) { 94 cachedGlyph->mIsValid = false; 95 } 96 } 97} 98 99void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, 100 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 101 int nPenX = x + glyph->mBitmapLeft; 102 int nPenY = y + glyph->mBitmapTop; 103 104 int width = (int) glyph->mBitmapWidth; 105 int height = (int) glyph->mBitmapHeight; 106 107 if (bounds->bottom > nPenY) { 108 bounds->bottom = nPenY; 109 } 110 if (bounds->left > nPenX) { 111 bounds->left = nPenX; 112 } 113 if (bounds->right < nPenX + width) { 114 bounds->right = nPenX + width; 115 } 116 if (bounds->top < nPenY + height) { 117 bounds->top = nPenY + height; 118 } 119} 120 121void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, 122 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 123 int nPenX = x + glyph->mBitmapLeft; 124 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; 125 126 float u1 = glyph->mBitmapMinU; 127 float u2 = glyph->mBitmapMaxU; 128 float v1 = glyph->mBitmapMinV; 129 float v2 = glyph->mBitmapMaxV; 130 131 int width = (int) glyph->mBitmapWidth; 132 int height = (int) glyph->mBitmapHeight; 133 134 mState->appendMeshQuad(nPenX, nPenY, u1, v2, 135 nPenX + width, nPenY, u2, v2, 136 nPenX + width, nPenY - height, u2, v1, 137 nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture); 138} 139 140void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, 141 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 142 int nPenX = x + glyph->mBitmapLeft; 143 int nPenY = y + glyph->mBitmapTop; 144 145 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth; 146 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight; 147 148 CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture; 149 uint32_t cacheWidth = cacheTexture->mWidth; 150 const uint8_t* cacheBuffer = cacheTexture->mTexture; 151 152 uint32_t cacheX = 0, cacheY = 0; 153 int32_t bX = 0, bY = 0; 154 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) { 155 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) { 156#if DEBUG_FONT_RENDERER 157 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { 158 ALOGE("Skipping invalid index"); 159 continue; 160 } 161#endif 162 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; 163 bitmap[bY * bitmapW + bX] = tempCol; 164 } 165 } 166} 167 168void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, 169 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) { 170 const float halfWidth = glyph->mBitmapWidth * 0.5f; 171 const float height = glyph->mBitmapHeight; 172 173 vOffset += glyph->mBitmapTop + height; 174 175 SkPoint destination[4]; 176 measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent); 177 178 // Move along the tangent and offset by the normal 179 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset, 180 -tangent->fY * halfWidth + tangent->fX * vOffset); 181 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset, 182 tangent->fY * halfWidth + tangent->fX * vOffset); 183 destination[2].set(destination[1].fX + tangent->fY * height, 184 destination[1].fY - tangent->fX * height); 185 destination[3].set(destination[0].fX + tangent->fY * height, 186 destination[0].fY - tangent->fX * height); 187 188 const float u1 = glyph->mBitmapMinU; 189 const float u2 = glyph->mBitmapMaxU; 190 const float v1 = glyph->mBitmapMinV; 191 const float v2 = glyph->mBitmapMaxV; 192 193 mState->appendRotatedMeshQuad( 194 position->fX + destination[0].fX, 195 position->fY + destination[0].fY, u1, v2, 196 position->fX + destination[1].fX, 197 position->fY + destination[1].fY, u2, v2, 198 position->fX + destination[2].fX, 199 position->fY + destination[2].fY, u2, v1, 200 position->fX + destination[3].fX, 201 position->fY + destination[3].fY, u1, v1, 202 glyph->mCachedTextureLine->mCacheTexture); 203} 204 205CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { 206 CachedGlyphInfo* cachedGlyph = NULL; 207 ssize_t index = mCachedGlyphs.indexOfKey(textUnit); 208 if (index >= 0) { 209 cachedGlyph = mCachedGlyphs.valueAt(index); 210 } else { 211 cachedGlyph = cacheGlyph(paint, textUnit); 212 } 213 214 // Is the glyph still in texture cache? 215 if (!cachedGlyph->mIsValid) { 216 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit); 217 updateGlyphCache(paint, skiaGlyph, cachedGlyph); 218 } 219 220 return cachedGlyph; 221} 222 223void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 224 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 225 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { 226 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, 227 bitmapW, bitmapH, NULL, NULL); 228 } else { 229 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 230 0, 0, NULL, NULL); 231 } 232} 233 234void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 235 int numGlyphs, int x, int y, const float* positions) { 236 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 237 0, 0, NULL, positions); 238} 239 240void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 241 int numGlyphs, SkPath* path, float hOffset, float vOffset) { 242 if (numGlyphs == 0 || text == NULL || len == 0) { 243 return; 244 } 245 246 text += start; 247 248 int glyphsCount = 0; 249 SkFixed prevRsbDelta = 0; 250 251 float penX = 0.0f; 252 253 SkPoint position; 254 SkVector tangent; 255 256 SkPathMeasure measure(*path, false); 257 float pathLength = SkScalarToFloat(measure.getLength()); 258 259 if (paint->getTextAlign() != SkPaint::kLeft_Align) { 260 float textWidth = SkScalarToFloat(paint->measureText(text, len)); 261 float pathOffset = pathLength; 262 if (paint->getTextAlign() == SkPaint::kCenter_Align) { 263 textWidth *= 0.5f; 264 pathOffset *= 0.5f; 265 } 266 penX += pathOffset - textWidth; 267 } 268 269 while (glyphsCount < numGlyphs && penX < pathLength) { 270 glyph_t glyph = GET_GLYPH(text); 271 272 if (IS_END_OF_STRING(glyph)) { 273 break; 274 } 275 276 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 277 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); 278 prevRsbDelta = cachedGlyph->mRsbDelta; 279 280 if (cachedGlyph->mIsValid) { 281 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent); 282 } 283 284 penX += SkFixedToFloat(cachedGlyph->mAdvanceX); 285 286 glyphsCount++; 287 } 288} 289 290void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 291 int numGlyphs, Rect *bounds) { 292 if (bounds == NULL) { 293 ALOGE("No return rectangle provided to measure text"); 294 return; 295 } 296 bounds->set(1e6, -1e6, -1e6, 1e6); 297 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL); 298} 299 300void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 301 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, 302 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { 303 if (numGlyphs == 0 || text == NULL || len == 0) { 304 return; 305 } 306 307 static RenderGlyph gRenderGlyph[] = { 308 &android::uirenderer::Font::drawCachedGlyph, 309 &android::uirenderer::Font::drawCachedGlyphBitmap, 310 &android::uirenderer::Font::measureCachedGlyph 311 }; 312 RenderGlyph render = gRenderGlyph[mode]; 313 314 text += start; 315 int glyphsCount = 0; 316 317 if (CC_LIKELY(positions == NULL)) { 318 SkFixed prevRsbDelta = 0; 319 320 float penX = x + 0.5f; 321 int penY = y; 322 323 while (glyphsCount < numGlyphs) { 324 glyph_t glyph = GET_GLYPH(text); 325 326 // Reached the end of the string 327 if (IS_END_OF_STRING(glyph)) { 328 break; 329 } 330 331 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 332 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); 333 prevRsbDelta = cachedGlyph->mRsbDelta; 334 335 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 336 if (cachedGlyph->mIsValid) { 337 (*this.*render)(cachedGlyph, (int) floorf(penX), penY, 338 bitmap, bitmapW, bitmapH, bounds, positions); 339 } 340 341 penX += SkFixedToFloat(cachedGlyph->mAdvanceX); 342 343 glyphsCount++; 344 } 345 } else { 346 const SkPaint::Align align = paint->getTextAlign(); 347 348 // This is for renderPosText() 349 while (glyphsCount < numGlyphs) { 350 glyph_t glyph = GET_GLYPH(text); 351 352 // Reached the end of the string 353 if (IS_END_OF_STRING(glyph)) { 354 break; 355 } 356 357 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 358 359 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 360 if (cachedGlyph->mIsValid) { 361 int penX = x + positions[(glyphsCount << 1)]; 362 int penY = y + positions[(glyphsCount << 1) + 1]; 363 364 switch (align) { 365 case SkPaint::kRight_Align: 366 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX); 367 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY); 368 break; 369 case SkPaint::kCenter_Align: 370 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1); 371 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1); 372 default: 373 break; 374 } 375 376 (*this.*render)(cachedGlyph, penX, penY, 377 bitmap, bitmapW, bitmapH, bounds, positions); 378 } 379 380 glyphsCount++; 381 } 382 } 383} 384 385void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { 386 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 387 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 388 glyph->mBitmapLeft = skiaGlyph.fLeft; 389 glyph->mBitmapTop = skiaGlyph.fTop; 390 glyph->mLsbDelta = skiaGlyph.fLsbDelta; 391 glyph->mRsbDelta = skiaGlyph.fRsbDelta; 392 393 uint32_t startX = 0; 394 uint32_t startY = 0; 395 396 // Get the bitmap for the glyph 397 paint->findImage(skiaGlyph); 398 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY); 399 400 if (!glyph->mIsValid) { 401 return; 402 } 403 404 uint32_t endX = startX + skiaGlyph.fWidth; 405 uint32_t endY = startY + skiaGlyph.fHeight; 406 407 glyph->mStartX = startX; 408 glyph->mStartY = startY; 409 glyph->mBitmapWidth = skiaGlyph.fWidth; 410 glyph->mBitmapHeight = skiaGlyph.fHeight; 411 412 uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth; 413 uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight; 414 415 glyph->mBitmapMinU = (float) startX / (float) cacheWidth; 416 glyph->mBitmapMinV = (float) startY / (float) cacheHeight; 417 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; 418 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; 419 420 mState->mUploadTexture = true; 421} 422 423CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { 424 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 425 mCachedGlyphs.add(glyph, newGlyph); 426 427 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph); 428 newGlyph->mGlyphIndex = skiaGlyph.fID; 429 newGlyph->mIsValid = false; 430 431 updateGlyphCache(paint, skiaGlyph, newGlyph); 432 433 return newGlyph; 434} 435 436Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize, 437 int flags, uint32_t italicStyle, uint32_t scaleX, 438 SkPaint::Style style, uint32_t strokeWidth) { 439 Vector<Font*> &activeFonts = state->mActiveFonts; 440 441 for (uint32_t i = 0; i < activeFonts.size(); i++) { 442 Font* font = activeFonts[i]; 443 if (font->mFontId == fontId && font->mFontSize == fontSize && 444 font->mFlags == flags && font->mItalicStyle == italicStyle && 445 font->mScaleX == scaleX && font->mStyle == style && 446 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) { 447 return font; 448 } 449 } 450 451 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, 452 scaleX, style, strokeWidth); 453 activeFonts.push(newFont); 454 return newFont; 455} 456 457/////////////////////////////////////////////////////////////////////////////// 458// FontRenderer 459/////////////////////////////////////////////////////////////////////////////// 460 461static bool sLogFontRendererCreate = true; 462 463FontRenderer::FontRenderer() { 464 if (sLogFontRendererCreate) { 465 INIT_LOGD("Creating FontRenderer"); 466 } 467 468 mGammaTable = NULL; 469 mInitialized = false; 470 mMaxNumberOfQuads = 1024; 471 mCurrentQuadIndex = 0; 472 473 mTextMeshPtr = NULL; 474 mCurrentCacheTexture = NULL; 475 mLastCacheTexture = NULL; 476 mCacheTextureSmall = NULL; 477 mCacheTexture128 = NULL; 478 mCacheTexture256 = NULL; 479 mCacheTexture512 = NULL; 480 481 mLinearFiltering = false; 482 483 mIndexBufferID = 0; 484 485 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 486 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 487 488 char property[PROPERTY_VALUE_MAX]; 489 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 490 if (sLogFontRendererCreate) { 491 INIT_LOGD(" Setting text cache width to %s pixels", property); 492 } 493 mSmallCacheWidth = atoi(property); 494 } else { 495 if (sLogFontRendererCreate) { 496 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth); 497 } 498 } 499 500 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 501 if (sLogFontRendererCreate) { 502 INIT_LOGD(" Setting text cache width to %s pixels", property); 503 } 504 mSmallCacheHeight = atoi(property); 505 } else { 506 if (sLogFontRendererCreate) { 507 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight); 508 } 509 } 510 511 sLogFontRendererCreate = false; 512} 513 514FontRenderer::~FontRenderer() { 515 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 516 delete mCacheLines[i]; 517 } 518 mCacheLines.clear(); 519 520 if (mInitialized) { 521 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers 522 Caches::getInstance().unbindIndicesBuffer(); 523 glDeleteBuffers(1, &mIndexBufferID); 524 525 delete[] mTextMeshPtr; 526 delete mCacheTextureSmall; 527 delete mCacheTexture128; 528 delete mCacheTexture256; 529 delete mCacheTexture512; 530 } 531 532 Vector<Font*> fontsToDereference = mActiveFonts; 533 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 534 delete fontsToDereference[i]; 535 } 536} 537 538void FontRenderer::flushAllAndInvalidate() { 539 if (mCurrentQuadIndex != 0) { 540 issueDrawCommand(); 541 mCurrentQuadIndex = 0; 542 } 543 544 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 545 mActiveFonts[i]->invalidateTextureCache(); 546 } 547 548 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 549 mCacheLines[i]->mCurrentCol = 0; 550 } 551} 552 553void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) { 554 if (cacheTexture && cacheTexture->mTexture) { 555 glDeleteTextures(1, &cacheTexture->mTextureId); 556 delete[] cacheTexture->mTexture; 557 cacheTexture->mTexture = NULL; 558 cacheTexture->mTextureId = 0; 559 } 560} 561 562void FontRenderer::flushLargeCaches() { 563 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) && 564 (!mCacheTexture256 || !mCacheTexture256->mTexture) && 565 (!mCacheTexture512 || !mCacheTexture512->mTexture)) { 566 // Typical case; no large glyph caches allocated 567 return; 568 } 569 570 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 571 CacheTextureLine* cacheLine = mCacheLines[i]; 572 if ((cacheLine->mCacheTexture == mCacheTexture128 || 573 cacheLine->mCacheTexture == mCacheTexture256 || 574 cacheLine->mCacheTexture == mCacheTexture512) && 575 cacheLine->mCacheTexture->mTexture != NULL) { 576 cacheLine->mCurrentCol = 0; 577 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 578 mActiveFonts[i]->invalidateTextureCache(cacheLine); 579 } 580 } 581 } 582 583 deallocateTextureMemory(mCacheTexture128); 584 deallocateTextureMemory(mCacheTexture256); 585 deallocateTextureMemory(mCacheTexture512); 586} 587 588void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) { 589 int width = cacheTexture->mWidth; 590 int height = cacheTexture->mHeight; 591 592 cacheTexture->mTexture = new uint8_t[width * height]; 593#if DEBUG_FONT_RENDERER 594 memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t)); 595#endif 596 597 if (!cacheTexture->mTextureId) { 598 glGenTextures(1, &cacheTexture->mTextureId); 599 } 600 601 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 uint8_t* textureMemory = NULL; 701 CacheTexture* cacheTexture = new CacheTexture(textureMemory, width, height); 702 703 if (allocate) { 704 allocateTextureMemory(cacheTexture); 705 } 706 707 return cacheTexture; 708} 709 710void FontRenderer::initTextTexture() { 711 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 712 delete mCacheLines[i]; 713 } 714 mCacheLines.clear(); 715 716 if (mCacheTextureSmall) { 717 delete mCacheTextureSmall; 718 delete mCacheTexture128; 719 delete mCacheTexture256; 720 delete mCacheTexture512; 721 } 722 723 // Next, use other, separate caches for large glyphs. 724 uint16_t maxWidth = 0; 725 if (Caches::hasInstance()) { 726 maxWidth = Caches::getInstance().maxTextureSize; 727 } 728 729 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) { 730 maxWidth = MAX_TEXT_CACHE_WIDTH; 731 } 732 733 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true); 734 mCacheTexture128 = createCacheTexture(maxWidth, 256, false); 735 mCacheTexture256 = createCacheTexture(maxWidth, 256, false); 736 mCacheTexture512 = createCacheTexture(maxWidth, 512, false); 737 mCurrentCacheTexture = mCacheTextureSmall; 738 739 mUploadTexture = false; 740 // Split up our default cache texture into lines of certain widths 741 int nextLine = 0; 742 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall)); 743 nextLine += mCacheLines.top()->mMaxHeight; 744 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 745 nextLine += mCacheLines.top()->mMaxHeight; 746 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 747 nextLine += mCacheLines.top()->mMaxHeight; 748 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 749 nextLine += mCacheLines.top()->mMaxHeight; 750 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 751 nextLine += mCacheLines.top()->mMaxHeight; 752 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall)); 753 nextLine += mCacheLines.top()->mMaxHeight; 754 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine, 755 nextLine, 0, mCacheTextureSmall)); 756 757 // The first cache is split into 2 lines of height 128, the rest have just one cache line. 758 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128)); 759 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128)); 760 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256)); 761 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512)); 762} 763 764// Avoid having to reallocate memory and render quad by quad 765void FontRenderer::initVertexArrayBuffers() { 766 uint32_t numIndices = mMaxNumberOfQuads * 6; 767 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 768 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 769 770 // Four verts, two triangles , six indices per quad 771 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 772 int i6 = i * 6; 773 int i4 = i * 4; 774 775 indexBufferData[i6 + 0] = i4 + 0; 776 indexBufferData[i6 + 1] = i4 + 1; 777 indexBufferData[i6 + 2] = i4 + 2; 778 779 indexBufferData[i6 + 3] = i4 + 0; 780 indexBufferData[i6 + 4] = i4 + 2; 781 indexBufferData[i6 + 5] = i4 + 3; 782 } 783 784 glGenBuffers(1, &mIndexBufferID); 785 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 786 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 787 788 free(indexBufferData); 789 790 uint32_t coordSize = 2; 791 uint32_t uvSize = 2; 792 uint32_t vertsPerQuad = 4; 793 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 794 mTextMeshPtr = new float[vertexBufferSize]; 795} 796 797// We don't want to allocate anything unless we actually draw text 798void FontRenderer::checkInit() { 799 if (mInitialized) { 800 return; 801 } 802 803 initTextTexture(); 804 initVertexArrayBuffers(); 805 806 mInitialized = true; 807} 808 809void FontRenderer::checkTextureUpdate() { 810 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) { 811 return; 812 } 813 814 Caches& caches = Caches::getInstance(); 815 GLuint lastTextureId = 0; 816 // Iterate over all the cache lines and see which ones need to be updated 817 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 818 CacheTextureLine* cl = mCacheLines[i]; 819 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) { 820 CacheTexture* cacheTexture = cl->mCacheTexture; 821 uint32_t xOffset = 0; 822 uint32_t yOffset = cl->mCurrentRow; 823 uint32_t width = cl->mMaxWidth; 824 uint32_t height = cl->mMaxHeight; 825 void* textureData = cacheTexture->mTexture + (yOffset * width); 826 827 if (cacheTexture->mTextureId != lastTextureId) { 828 caches.activeTexture(0); 829 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 830 lastTextureId = cacheTexture->mTextureId; 831 } 832 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 833 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 834 835 cl->mDirty = false; 836 } 837 } 838 839 caches.activeTexture(0); 840 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); 841 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) { 842 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST; 843 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 844 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 845 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering; 846 } 847 mLastCacheTexture = mCurrentCacheTexture; 848 849 mUploadTexture = false; 850} 851 852void FontRenderer::issueDrawCommand() { 853 checkTextureUpdate(); 854 855 Caches& caches = Caches::getInstance(); 856 caches.bindIndicesBuffer(mIndexBufferID); 857 if (!mDrawn) { 858 float* buffer = mTextMeshPtr; 859 int offset = 2; 860 861 bool force = caches.unbindMeshBuffer(); 862 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 863 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 864 buffer + offset); 865 } 866 867 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 868 869 mDrawn = true; 870} 871 872void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 873 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 874 float x4, float y4, float u4, float v4, CacheTexture* texture) { 875 if (texture != mCurrentCacheTexture) { 876 if (mCurrentQuadIndex != 0) { 877 // First, draw everything stored already which uses the previous texture 878 issueDrawCommand(); 879 mCurrentQuadIndex = 0; 880 } 881 // Now use the new texture id 882 mCurrentCacheTexture = texture; 883 } 884 885 const uint32_t vertsPerQuad = 4; 886 const uint32_t floatsPerVert = 4; 887 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 888 889 (*currentPos++) = x1; 890 (*currentPos++) = y1; 891 (*currentPos++) = u1; 892 (*currentPos++) = v1; 893 894 (*currentPos++) = x2; 895 (*currentPos++) = y2; 896 (*currentPos++) = u2; 897 (*currentPos++) = v2; 898 899 (*currentPos++) = x3; 900 (*currentPos++) = y3; 901 (*currentPos++) = u3; 902 (*currentPos++) = v3; 903 904 (*currentPos++) = x4; 905 (*currentPos++) = y4; 906 (*currentPos++) = u4; 907 (*currentPos++) = v4; 908 909 mCurrentQuadIndex++; 910} 911 912void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 913 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 914 float x4, float y4, float u4, float v4, CacheTexture* texture) { 915 916 if (mClip && 917 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 918 return; 919 } 920 921 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 922 923 if (mBounds) { 924 mBounds->left = fmin(mBounds->left, x1); 925 mBounds->top = fmin(mBounds->top, y3); 926 mBounds->right = fmax(mBounds->right, x3); 927 mBounds->bottom = fmax(mBounds->bottom, y1); 928 } 929 930 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 931 issueDrawCommand(); 932 mCurrentQuadIndex = 0; 933 } 934} 935 936void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 937 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 938 float x4, float y4, float u4, float v4, CacheTexture* texture) { 939 940 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 941 942 if (mBounds) { 943 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 944 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 945 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 946 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 947 } 948 949 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 950 issueDrawCommand(); 951 mCurrentQuadIndex = 0; 952 } 953} 954 955uint32_t FontRenderer::getRemainingCacheCapacity() { 956 uint32_t remainingCapacity = 0; 957 float totalPixels = 0; 958 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 959 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 960 totalPixels += mCacheLines[i]->mMaxWidth; 961 } 962 remainingCapacity = (remainingCapacity * 100) / totalPixels; 963 return remainingCapacity; 964} 965 966void FontRenderer::precacheLatin(SkPaint* paint) { 967 // Remaining capacity is measured in % 968 uint32_t remainingCapacity = getRemainingCacheCapacity(); 969 uint32_t precacheIndex = 0; 970 971 // We store a string with letters in a rough frequency of occurrence 972 String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789"); 973 974 size_t size = l.size(); 975 uint16_t latin[size]; 976 paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin); 977 978 while (remainingCapacity > 25 && precacheIndex < size) { 979 mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex])); 980 remainingCapacity = getRemainingCacheCapacity(); 981 precacheIndex++; 982 } 983} 984 985void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 986 uint32_t currentNumFonts = mActiveFonts.size(); 987 int flags = 0; 988 if (paint->isFakeBoldText()) { 989 flags |= Font::kFakeBold; 990 } 991 992 const float skewX = paint->getTextSkewX(); 993 uint32_t italicStyle = *(uint32_t*) &skewX; 994 const float scaleXFloat = paint->getTextScaleX(); 995 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 996 SkPaint::Style style = paint->getStyle(); 997 const float strokeWidthFloat = paint->getStrokeWidth(); 998 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 999 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 1000 scaleX, style, strokeWidth); 1001 1002 const float maxPrecacheFontSize = 40.0f; 1003 bool isNewFont = currentNumFonts != mActiveFonts.size(); 1004 1005 if (isNewFont && fontSize <= maxPrecacheFontSize) { 1006 precacheLatin(paint); 1007 } 1008} 1009 1010FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 1011 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { 1012 checkInit(); 1013 1014 if (!mCurrentFont) { 1015 DropShadow image; 1016 image.width = 0; 1017 image.height = 0; 1018 image.image = NULL; 1019 image.penX = 0; 1020 image.penY = 0; 1021 return image; 1022 } 1023 1024 mDrawn = false; 1025 mClip = NULL; 1026 mBounds = NULL; 1027 1028 Rect bounds; 1029 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds); 1030 1031 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 1032 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 1033 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 1034 1035 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 1036 dataBuffer[i] = 0; 1037 } 1038 1039 int penX = radius - bounds.left; 1040 int penY = radius - bounds.bottom; 1041 1042 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 1043 dataBuffer, paddedWidth, paddedHeight); 1044 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 1045 1046 DropShadow image; 1047 image.width = paddedWidth; 1048 image.height = paddedHeight; 1049 image.image = dataBuffer; 1050 image.penX = penX; 1051 image.penY = penY; 1052 1053 return image; 1054} 1055 1056void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 1057 checkInit(); 1058 1059 mDrawn = false; 1060 mBounds = bounds; 1061 mClip = clip; 1062} 1063 1064void FontRenderer::finishRender() { 1065 mBounds = NULL; 1066 mClip = NULL; 1067 1068 if (mCurrentQuadIndex != 0) { 1069 issueDrawCommand(); 1070 mCurrentQuadIndex = 0; 1071 } 1072} 1073 1074bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 1075 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 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); 1083 finishRender(); 1084 1085 return mDrawn; 1086} 1087 1088bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 1089 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 1090 const float* positions, 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, x, y, positions); 1098 finishRender(); 1099 1100 return mDrawn; 1101} 1102 1103bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 1104 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 1105 float hOffset, float vOffset, Rect* bounds) { 1106 if (!mCurrentFont) { 1107 ALOGE("No font set"); 1108 return false; 1109 } 1110 1111 initRender(clip, bounds); 1112 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 1113 finishRender(); 1114 1115 return mDrawn; 1116} 1117 1118void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 1119 // Compute gaussian weights for the blur 1120 // e is the euler's number 1121 float e = 2.718281828459045f; 1122 float pi = 3.1415926535897932f; 1123 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 1124 // x is of the form [-radius .. 0 .. radius] 1125 // and sigma varies with radius. 1126 // Based on some experimental radius values and sigma's 1127 // we approximately fit sigma = f(radius) as 1128 // sigma = radius * 0.3 + 0.6 1129 // The larger the radius gets, the more our gaussian blur 1130 // will resemble a box blur since with large sigma 1131 // the gaussian curve begins to lose its shape 1132 float sigma = 0.3f * (float) radius + 0.6f; 1133 1134 // Now compute the coefficints 1135 // We will store some redundant values to save some math during 1136 // the blur calculations 1137 // precompute some values 1138 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 1139 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 1140 1141 float normalizeFactor = 0.0f; 1142 for (int32_t r = -radius; r <= radius; r ++) { 1143 float floatR = (float) r; 1144 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 1145 normalizeFactor += weights[r + radius]; 1146 } 1147 1148 //Now we need to normalize the weights because all our coefficients need to add up to one 1149 normalizeFactor = 1.0f / normalizeFactor; 1150 for (int32_t r = -radius; r <= radius; r ++) { 1151 weights[r + radius] *= normalizeFactor; 1152 } 1153} 1154 1155void FontRenderer::horizontalBlur(float* weights, int32_t radius, 1156 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1157 float blurredPixel = 0.0f; 1158 float currentPixel = 0.0f; 1159 1160 for (int32_t y = 0; y < height; y ++) { 1161 1162 const uint8_t* input = source + y * width; 1163 uint8_t* output = dest + y * width; 1164 1165 for (int32_t x = 0; x < width; x ++) { 1166 blurredPixel = 0.0f; 1167 const float* gPtr = weights; 1168 // Optimization for non-border pixels 1169 if (x > radius && x < (width - radius)) { 1170 const uint8_t *i = input + (x - radius); 1171 for (int r = -radius; r <= radius; r ++) { 1172 currentPixel = (float) (*i); 1173 blurredPixel += currentPixel * gPtr[0]; 1174 gPtr++; 1175 i++; 1176 } 1177 } else { 1178 for (int32_t r = -radius; r <= radius; r ++) { 1179 // Stepping left and right away from the pixel 1180 int validW = x + r; 1181 if (validW < 0) { 1182 validW = 0; 1183 } 1184 if (validW > width - 1) { 1185 validW = width - 1; 1186 } 1187 1188 currentPixel = (float) input[validW]; 1189 blurredPixel += currentPixel * gPtr[0]; 1190 gPtr++; 1191 } 1192 } 1193 *output = (uint8_t)blurredPixel; 1194 output ++; 1195 } 1196 } 1197} 1198 1199void FontRenderer::verticalBlur(float* weights, int32_t radius, 1200 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1201 float blurredPixel = 0.0f; 1202 float currentPixel = 0.0f; 1203 1204 for (int32_t y = 0; y < height; y ++) { 1205 1206 uint8_t* output = dest + y * width; 1207 1208 for (int32_t x = 0; x < width; x ++) { 1209 blurredPixel = 0.0f; 1210 const float* gPtr = weights; 1211 const uint8_t* input = source + x; 1212 // Optimization for non-border pixels 1213 if (y > radius && y < (height - radius)) { 1214 const uint8_t *i = input + ((y - radius) * width); 1215 for (int32_t r = -radius; r <= radius; r ++) { 1216 currentPixel = (float)(*i); 1217 blurredPixel += currentPixel * gPtr[0]; 1218 gPtr++; 1219 i += width; 1220 } 1221 } else { 1222 for (int32_t r = -radius; r <= radius; r ++) { 1223 int validH = y + r; 1224 // Clamp to zero and width 1225 if (validH < 0) { 1226 validH = 0; 1227 } 1228 if (validH > height - 1) { 1229 validH = height - 1; 1230 } 1231 1232 const uint8_t *i = input + validH * width; 1233 currentPixel = (float) (*i); 1234 blurredPixel += currentPixel * gPtr[0]; 1235 gPtr++; 1236 } 1237 } 1238 *output = (uint8_t) blurredPixel; 1239 output ++; 1240 } 1241 } 1242} 1243 1244 1245void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 1246 float *gaussian = new float[2 * radius + 1]; 1247 computeGaussianWeights(gaussian, radius); 1248 1249 uint8_t* scratch = new uint8_t[width * height]; 1250 1251 horizontalBlur(gaussian, radius, image, scratch, width, height); 1252 verticalBlur(gaussian, radius, scratch, image, width, height); 1253 1254 delete[] gaussian; 1255 delete[] scratch; 1256} 1257 1258}; // namespace uirenderer 1259}; // namespace android 1260