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 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 950 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 951 totalPixels += mCacheLines[i]->mMaxWidth; 952 } 953 remainingCapacity = (remainingCapacity * 100) / totalPixels; 954 return remainingCapacity; 955} 956 957void FontRenderer::precacheLatin(SkPaint* paint) { 958 // Remaining capacity is measured in % 959 uint32_t remainingCapacity = getRemainingCacheCapacity(); 960 uint32_t precacheIndex = 0; 961 962 // We store a string with letters in a rough frequency of occurrence 963 String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789"); 964 965 size_t size = l.size(); 966 uint16_t latin[size]; 967 paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin); 968 969 while (remainingCapacity > 25 && precacheIndex < size) { 970 mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex])); 971 remainingCapacity = getRemainingCacheCapacity(); 972 precacheIndex++; 973 } 974} 975 976void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 977 uint32_t currentNumFonts = mActiveFonts.size(); 978 int flags = 0; 979 if (paint->isFakeBoldText()) { 980 flags |= Font::kFakeBold; 981 } 982 983 const float skewX = paint->getTextSkewX(); 984 uint32_t italicStyle = *(uint32_t*) &skewX; 985 const float scaleXFloat = paint->getTextScaleX(); 986 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 987 SkPaint::Style style = paint->getStyle(); 988 const float strokeWidthFloat = paint->getStrokeWidth(); 989 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 990 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 991 scaleX, style, strokeWidth); 992 993 const float maxPrecacheFontSize = 40.0f; 994 bool isNewFont = currentNumFonts != mActiveFonts.size(); 995 996 if (isNewFont && fontSize <= maxPrecacheFontSize) { 997 precacheLatin(paint); 998 } 999} 1000 1001FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 1002 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { 1003 checkInit(); 1004 1005 if (!mCurrentFont) { 1006 DropShadow image; 1007 image.width = 0; 1008 image.height = 0; 1009 image.image = NULL; 1010 image.penX = 0; 1011 image.penY = 0; 1012 return image; 1013 } 1014 1015 mDrawn = false; 1016 mClip = NULL; 1017 mBounds = NULL; 1018 1019 Rect bounds; 1020 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds); 1021 1022 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 1023 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 1024 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 1025 1026 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 1027 dataBuffer[i] = 0; 1028 } 1029 1030 int penX = radius - bounds.left; 1031 int penY = radius - bounds.bottom; 1032 1033 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 1034 dataBuffer, paddedWidth, paddedHeight); 1035 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 1036 1037 DropShadow image; 1038 image.width = paddedWidth; 1039 image.height = paddedHeight; 1040 image.image = dataBuffer; 1041 image.penX = penX; 1042 image.penY = penY; 1043 1044 return image; 1045} 1046 1047void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 1048 checkInit(); 1049 1050 mDrawn = false; 1051 mBounds = bounds; 1052 mClip = clip; 1053} 1054 1055void FontRenderer::finishRender() { 1056 mBounds = NULL; 1057 mClip = NULL; 1058 1059 if (mCurrentQuadIndex != 0) { 1060 issueDrawCommand(); 1061 mCurrentQuadIndex = 0; 1062 } 1063} 1064 1065bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 1066 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 1067 if (!mCurrentFont) { 1068 ALOGE("No font set"); 1069 return false; 1070 } 1071 1072 initRender(clip, bounds); 1073 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 1074 finishRender(); 1075 1076 return mDrawn; 1077} 1078 1079bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 1080 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 1081 const float* positions, Rect* bounds) { 1082 if (!mCurrentFont) { 1083 ALOGE("No font set"); 1084 return false; 1085 } 1086 1087 initRender(clip, bounds); 1088 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 1089 finishRender(); 1090 1091 return mDrawn; 1092} 1093 1094bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 1095 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 1096 float hOffset, float vOffset, Rect* bounds) { 1097 if (!mCurrentFont) { 1098 ALOGE("No font set"); 1099 return false; 1100 } 1101 1102 initRender(clip, bounds); 1103 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 1104 finishRender(); 1105 1106 return mDrawn; 1107} 1108 1109void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 1110 // Compute gaussian weights for the blur 1111 // e is the euler's number 1112 float e = 2.718281828459045f; 1113 float pi = 3.1415926535897932f; 1114 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 1115 // x is of the form [-radius .. 0 .. radius] 1116 // and sigma varies with radius. 1117 // Based on some experimental radius values and sigma's 1118 // we approximately fit sigma = f(radius) as 1119 // sigma = radius * 0.3 + 0.6 1120 // The larger the radius gets, the more our gaussian blur 1121 // will resemble a box blur since with large sigma 1122 // the gaussian curve begins to lose its shape 1123 float sigma = 0.3f * (float) radius + 0.6f; 1124 1125 // Now compute the coefficints 1126 // We will store some redundant values to save some math during 1127 // the blur calculations 1128 // precompute some values 1129 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 1130 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 1131 1132 float normalizeFactor = 0.0f; 1133 for (int32_t r = -radius; r <= radius; r ++) { 1134 float floatR = (float) r; 1135 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 1136 normalizeFactor += weights[r + radius]; 1137 } 1138 1139 //Now we need to normalize the weights because all our coefficients need to add up to one 1140 normalizeFactor = 1.0f / normalizeFactor; 1141 for (int32_t r = -radius; r <= radius; r ++) { 1142 weights[r + radius] *= normalizeFactor; 1143 } 1144} 1145 1146void FontRenderer::horizontalBlur(float* weights, int32_t radius, 1147 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1148 float blurredPixel = 0.0f; 1149 float currentPixel = 0.0f; 1150 1151 for (int32_t y = 0; y < height; y ++) { 1152 1153 const uint8_t* input = source + y * width; 1154 uint8_t* output = dest + y * width; 1155 1156 for (int32_t x = 0; x < width; x ++) { 1157 blurredPixel = 0.0f; 1158 const float* gPtr = weights; 1159 // Optimization for non-border pixels 1160 if (x > radius && x < (width - radius)) { 1161 const uint8_t *i = input + (x - radius); 1162 for (int r = -radius; r <= radius; r ++) { 1163 currentPixel = (float) (*i); 1164 blurredPixel += currentPixel * gPtr[0]; 1165 gPtr++; 1166 i++; 1167 } 1168 } else { 1169 for (int32_t r = -radius; r <= radius; r ++) { 1170 // Stepping left and right away from the pixel 1171 int validW = x + r; 1172 if (validW < 0) { 1173 validW = 0; 1174 } 1175 if (validW > width - 1) { 1176 validW = width - 1; 1177 } 1178 1179 currentPixel = (float) input[validW]; 1180 blurredPixel += currentPixel * gPtr[0]; 1181 gPtr++; 1182 } 1183 } 1184 *output = (uint8_t)blurredPixel; 1185 output ++; 1186 } 1187 } 1188} 1189 1190void FontRenderer::verticalBlur(float* weights, int32_t radius, 1191 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1192 float blurredPixel = 0.0f; 1193 float currentPixel = 0.0f; 1194 1195 for (int32_t y = 0; y < height; y ++) { 1196 1197 uint8_t* output = dest + y * width; 1198 1199 for (int32_t x = 0; x < width; x ++) { 1200 blurredPixel = 0.0f; 1201 const float* gPtr = weights; 1202 const uint8_t* input = source + x; 1203 // Optimization for non-border pixels 1204 if (y > radius && y < (height - radius)) { 1205 const uint8_t *i = input + ((y - radius) * width); 1206 for (int32_t r = -radius; r <= radius; r ++) { 1207 currentPixel = (float)(*i); 1208 blurredPixel += currentPixel * gPtr[0]; 1209 gPtr++; 1210 i += width; 1211 } 1212 } else { 1213 for (int32_t r = -radius; r <= radius; r ++) { 1214 int validH = y + r; 1215 // Clamp to zero and width 1216 if (validH < 0) { 1217 validH = 0; 1218 } 1219 if (validH > height - 1) { 1220 validH = height - 1; 1221 } 1222 1223 const uint8_t *i = input + validH * width; 1224 currentPixel = (float) (*i); 1225 blurredPixel += currentPixel * gPtr[0]; 1226 gPtr++; 1227 } 1228 } 1229 *output = (uint8_t) blurredPixel; 1230 output ++; 1231 } 1232 } 1233} 1234 1235 1236void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 1237 float *gaussian = new float[2 * radius + 1]; 1238 computeGaussianWeights(gaussian, radius); 1239 1240 uint8_t* scratch = new uint8_t[width * height]; 1241 1242 horizontalBlur(gaussian, radius, image, scratch, width, height); 1243 verticalBlur(gaussian, radius, scratch, image, width, height); 1244 1245 delete[] gaussian; 1246 delete[] scratch; 1247} 1248 1249}; // namespace uirenderer 1250}; // namespace android 1251