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