FontRenderer.cpp revision 378e919ccb75efe24d5a5aa75ac2c6ef255dcb48
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 CACHE_BLOCK_ROUNDING_SIZE 4 41 42#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16) 43 44/////////////////////////////////////////////////////////////////////////////// 45// CacheBlock 46/////////////////////////////////////////////////////////////////////////////// 47 48/** 49 * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width 50 * order, except for the final block (the remainder space at the right, since we fill from the 51 * left). 52 */ 53CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) { 54#if DEBUG_FONT_RENDERER 55 ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d", 56 newBlock, newBlock->mX, newBlock->mY, 57 newBlock->mWidth, newBlock->mHeight); 58#endif 59 CacheBlock *currBlock = head; 60 CacheBlock *prevBlock = NULL; 61 while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) { 62 if (newBlock->mWidth < currBlock->mWidth) { 63 newBlock->mNext = currBlock; 64 newBlock->mPrev = prevBlock; 65 currBlock->mPrev = newBlock; 66 if (prevBlock) { 67 prevBlock->mNext = newBlock; 68 return head; 69 } else { 70 return newBlock; 71 } 72 } 73 prevBlock = currBlock; 74 currBlock = currBlock->mNext; 75 } 76 // new block larger than all others - insert at end (but before the remainder space, if there) 77 newBlock->mNext = currBlock; 78 newBlock->mPrev = prevBlock; 79 if (currBlock) { 80 currBlock->mPrev = newBlock; 81 } 82 if (prevBlock) { 83 prevBlock->mNext = newBlock; 84 return head; 85 } else { 86 return newBlock; 87 } 88} 89 90CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) { 91#if DEBUG_FONT_RENDERER 92 ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d", 93 blockToRemove, blockToRemove->mX, blockToRemove->mY, 94 blockToRemove->mWidth, blockToRemove->mHeight); 95#endif 96 CacheBlock* newHead = head; 97 CacheBlock* nextBlock = blockToRemove->mNext; 98 CacheBlock* prevBlock = blockToRemove->mPrev; 99 if (prevBlock) { 100 prevBlock->mNext = nextBlock; 101 } else { 102 newHead = nextBlock; 103 } 104 if (nextBlock) { 105 nextBlock->mPrev = prevBlock; 106 } 107 delete blockToRemove; 108 return newHead; 109} 110 111/////////////////////////////////////////////////////////////////////////////// 112// CacheTexture 113/////////////////////////////////////////////////////////////////////////////// 114 115bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { 116 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) { 117 return false; 118 } 119 120 uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE; 121 uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE; 122 // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE. 123 // This columns for glyphs that are close but not necessarily exactly the same size. It trades 124 // off the loss of a few pixels for some glyphs against the ability to store more glyphs 125 // of varying sizes in one block. 126 uint16_t roundedUpW = 127 (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE; 128 CacheBlock *cacheBlock = mCacheBlocks; 129 while (cacheBlock) { 130 // Store glyph in this block iff: it fits the block's remaining space and: 131 // it's the remainder space (mY == 0) or there's only enough height for this one glyph 132 // or it's within ROUNDING_SIZE of the block width 133 if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight && 134 (cacheBlock->mY == TEXTURE_BORDER_SIZE || 135 (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) { 136 if (cacheBlock->mHeight - glyphH < glyphH) { 137 // Only enough space for this glyph - don't bother rounding up the width 138 roundedUpW = glyphW; 139 } 140 *retOriginX = cacheBlock->mX; 141 *retOriginY = cacheBlock->mY; 142 // If this is the remainder space, create a new cache block for this column. Otherwise, 143 // adjust the info about this column. 144 if (cacheBlock->mY == TEXTURE_BORDER_SIZE) { 145 uint16_t oldX = cacheBlock->mX; 146 // Adjust remainder space dimensions 147 cacheBlock->mWidth -= roundedUpW; 148 cacheBlock->mX += roundedUpW; 149 if (mHeight - glyphH >= glyphH) { 150 // There's enough height left over to create a new CacheBlock 151 CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW, 152 mHeight - glyphH); 153#if DEBUG_FONT_RENDERER 154 ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d", 155 newBlock, newBlock->mX, newBlock->mY, 156 newBlock->mWidth, newBlock->mHeight); 157#endif 158 mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock); 159 } 160 } else { 161 // Insert into current column and adjust column dimensions 162 cacheBlock->mY += glyphH; 163 cacheBlock->mHeight -= glyphH; 164#if DEBUG_FONT_RENDERER 165 ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d", 166 cacheBlock, cacheBlock->mX, cacheBlock->mY, 167 cacheBlock->mWidth, cacheBlock->mHeight); 168#endif 169 } 170 if (cacheBlock->mHeight < fmin(glyphH, glyphW)) { 171 // If remaining space in this block is too small to be useful, remove it 172 mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock); 173 } 174 mDirty = true; 175#if DEBUG_FONT_RENDERER 176 ALOGD("fitBitmap: current block list:"); 177 mCacheBlocks->output(); 178#endif 179 ++mNumGlyphs; 180 return true; 181 } 182 cacheBlock = cacheBlock->mNext; 183 } 184#if DEBUG_FONT_RENDERER 185 ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH); 186#endif 187 return false; 188} 189 190/////////////////////////////////////////////////////////////////////////////// 191// Font 192/////////////////////////////////////////////////////////////////////////////// 193 194Font::Font(FontRenderer* state, uint32_t fontId, float fontSize, 195 int flags, uint32_t italicStyle, uint32_t scaleX, 196 SkPaint::Style style, uint32_t strokeWidth) : 197 mState(state), mFontId(fontId), mFontSize(fontSize), 198 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX), 199 mStyle(style), mStrokeWidth(mStrokeWidth) { 200} 201 202 203Font::~Font() { 204 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { 205 if (mState->mActiveFonts[ct] == this) { 206 mState->mActiveFonts.removeAt(ct); 207 break; 208 } 209 } 210 211 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 212 delete mCachedGlyphs.valueAt(i); 213 } 214} 215 216void Font::invalidateTextureCache(CacheTexture *cacheTexture) { 217 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 218 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i); 219 if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) { 220 cachedGlyph->mIsValid = false; 221 } 222 } 223} 224 225void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, 226 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 227 int nPenX = x + glyph->mBitmapLeft; 228 int nPenY = y + glyph->mBitmapTop; 229 230 int width = (int) glyph->mBitmapWidth; 231 int height = (int) glyph->mBitmapHeight; 232 233 if (bounds->bottom > nPenY) { 234 bounds->bottom = nPenY; 235 } 236 if (bounds->left > nPenX) { 237 bounds->left = nPenX; 238 } 239 if (bounds->right < nPenX + width) { 240 bounds->right = nPenX + width; 241 } 242 if (bounds->top < nPenY + height) { 243 bounds->top = nPenY + height; 244 } 245} 246 247void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, 248 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 249 int nPenX = x + glyph->mBitmapLeft; 250 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; 251 252 float u1 = glyph->mBitmapMinU; 253 float u2 = glyph->mBitmapMaxU; 254 float v1 = glyph->mBitmapMinV; 255 float v2 = glyph->mBitmapMaxV; 256 257 int width = (int) glyph->mBitmapWidth; 258 int height = (int) glyph->mBitmapHeight; 259 260 mState->appendMeshQuad(nPenX, nPenY, u1, v2, 261 nPenX + width, nPenY, u2, v2, 262 nPenX + width, nPenY - height, u2, v1, 263 nPenX, nPenY - height, u1, v1, glyph->mCacheTexture); 264} 265 266void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, 267 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 268 int nPenX = x + glyph->mBitmapLeft; 269 int nPenY = y + glyph->mBitmapTop; 270 271 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth; 272 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight; 273 274 CacheTexture *cacheTexture = glyph->mCacheTexture; 275 uint32_t cacheWidth = cacheTexture->mWidth; 276 const uint8_t* cacheBuffer = cacheTexture->mTexture; 277 278 uint32_t cacheX = 0, cacheY = 0; 279 int32_t bX = 0, bY = 0; 280 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) { 281 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) { 282#if DEBUG_FONT_RENDERER 283 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { 284 ALOGE("Skipping invalid index"); 285 continue; 286 } 287#endif 288 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; 289 bitmap[bY * bitmapW + bX] = tempCol; 290 } 291 } 292} 293 294void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, 295 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) { 296 const float halfWidth = glyph->mBitmapWidth * 0.5f; 297 const float height = glyph->mBitmapHeight; 298 299 vOffset += glyph->mBitmapTop + height; 300 301 SkPoint destination[4]; 302 measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent); 303 304 // Move along the tangent and offset by the normal 305 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset, 306 -tangent->fY * halfWidth + tangent->fX * vOffset); 307 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset, 308 tangent->fY * halfWidth + tangent->fX * vOffset); 309 destination[2].set(destination[1].fX + tangent->fY * height, 310 destination[1].fY - tangent->fX * height); 311 destination[3].set(destination[0].fX + tangent->fY * height, 312 destination[0].fY - tangent->fX * height); 313 314 const float u1 = glyph->mBitmapMinU; 315 const float u2 = glyph->mBitmapMaxU; 316 const float v1 = glyph->mBitmapMinV; 317 const float v2 = glyph->mBitmapMaxV; 318 319 mState->appendRotatedMeshQuad( 320 position->fX + destination[0].fX, 321 position->fY + destination[0].fY, u1, v2, 322 position->fX + destination[1].fX, 323 position->fY + destination[1].fY, u2, v2, 324 position->fX + destination[2].fX, 325 position->fY + destination[2].fY, u2, v1, 326 position->fX + destination[3].fX, 327 position->fY + destination[3].fY, u1, v1, 328 glyph->mCacheTexture); 329} 330 331CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { 332 CachedGlyphInfo* cachedGlyph = NULL; 333 ssize_t index = mCachedGlyphs.indexOfKey(textUnit); 334 if (index >= 0) { 335 cachedGlyph = mCachedGlyphs.valueAt(index); 336 } else { 337 cachedGlyph = cacheGlyph(paint, textUnit); 338 } 339 340 // Is the glyph still in texture cache? 341 if (!cachedGlyph->mIsValid) { 342 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit); 343 updateGlyphCache(paint, skiaGlyph, cachedGlyph); 344 } 345 346 return cachedGlyph; 347} 348 349void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 350 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 351 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { 352 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, 353 bitmapW, bitmapH, NULL, NULL); 354 } else { 355 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 356 0, 0, NULL, NULL); 357 } 358} 359 360void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 361 int numGlyphs, int x, int y, const float* positions) { 362 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 363 0, 0, NULL, positions); 364} 365 366void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 367 int numGlyphs, SkPath* path, float hOffset, float vOffset) { 368 if (numGlyphs == 0 || text == NULL || len == 0) { 369 return; 370 } 371 372 text += start; 373 374 int glyphsCount = 0; 375 SkFixed prevRsbDelta = 0; 376 377 float penX = 0.0f; 378 379 SkPoint position; 380 SkVector tangent; 381 382 SkPathMeasure measure(*path, false); 383 float pathLength = SkScalarToFloat(measure.getLength()); 384 385 if (paint->getTextAlign() != SkPaint::kLeft_Align) { 386 float textWidth = SkScalarToFloat(paint->measureText(text, len)); 387 float pathOffset = pathLength; 388 if (paint->getTextAlign() == SkPaint::kCenter_Align) { 389 textWidth *= 0.5f; 390 pathOffset *= 0.5f; 391 } 392 penX += pathOffset - textWidth; 393 } 394 395 while (glyphsCount < numGlyphs && penX < pathLength) { 396 glyph_t glyph = GET_GLYPH(text); 397 398 if (IS_END_OF_STRING(glyph)) { 399 break; 400 } 401 402 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 403 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); 404 prevRsbDelta = cachedGlyph->mRsbDelta; 405 406 if (cachedGlyph->mIsValid) { 407 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent); 408 } 409 410 penX += SkFixedToFloat(cachedGlyph->mAdvanceX); 411 412 glyphsCount++; 413 } 414} 415 416void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 417 int numGlyphs, Rect *bounds, const float* positions) { 418 if (bounds == NULL) { 419 ALOGE("No return rectangle provided to measure text"); 420 return; 421 } 422 bounds->set(1e6, -1e6, -1e6, 1e6); 423 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions); 424} 425 426void Font::precache(SkPaint* paint, const char* text, int numGlyphs) { 427 428 if (numGlyphs == 0 || text == NULL) { 429 return; 430 } 431 int glyphsCount = 0; 432 433 while (glyphsCount < numGlyphs) { 434 glyph_t glyph = GET_GLYPH(text); 435 436 // Reached the end of the string 437 if (IS_END_OF_STRING(glyph)) { 438 break; 439 } 440 441 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 442 443 glyphsCount++; 444 } 445} 446 447void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 448 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, 449 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { 450 if (numGlyphs == 0 || text == NULL || len == 0) { 451 return; 452 } 453 454 static RenderGlyph gRenderGlyph[] = { 455 &android::uirenderer::Font::drawCachedGlyph, 456 &android::uirenderer::Font::drawCachedGlyphBitmap, 457 &android::uirenderer::Font::measureCachedGlyph 458 }; 459 RenderGlyph render = gRenderGlyph[mode]; 460 461 text += start; 462 int glyphsCount = 0; 463 464 if (CC_LIKELY(positions == NULL)) { 465 SkFixed prevRsbDelta = 0; 466 467 float penX = x + 0.5f; 468 int penY = y; 469 470 while (glyphsCount < numGlyphs) { 471 glyph_t glyph = GET_GLYPH(text); 472 473 // Reached the end of the string 474 if (IS_END_OF_STRING(glyph)) { 475 break; 476 } 477 478 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 479 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); 480 prevRsbDelta = cachedGlyph->mRsbDelta; 481 482 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 483 if (cachedGlyph->mIsValid) { 484 (*this.*render)(cachedGlyph, (int) floorf(penX), penY, 485 bitmap, bitmapW, bitmapH, bounds, positions); 486 } 487 488 penX += SkFixedToFloat(cachedGlyph->mAdvanceX); 489 490 glyphsCount++; 491 } 492 } else { 493 const SkPaint::Align align = paint->getTextAlign(); 494 495 // This is for renderPosText() 496 while (glyphsCount < numGlyphs) { 497 glyph_t glyph = GET_GLYPH(text); 498 499 // Reached the end of the string 500 if (IS_END_OF_STRING(glyph)) { 501 break; 502 } 503 504 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 505 506 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 507 if (cachedGlyph->mIsValid) { 508 int penX = x + positions[(glyphsCount << 1)]; 509 int penY = y + positions[(glyphsCount << 1) + 1]; 510 511 switch (align) { 512 case SkPaint::kRight_Align: 513 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX); 514 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY); 515 break; 516 case SkPaint::kCenter_Align: 517 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1); 518 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1); 519 default: 520 break; 521 } 522 523 (*this.*render)(cachedGlyph, penX, penY, 524 bitmap, bitmapW, bitmapH, bounds, positions); 525 } 526 527 glyphsCount++; 528 } 529 } 530} 531 532void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { 533 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 534 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 535 glyph->mBitmapLeft = skiaGlyph.fLeft; 536 glyph->mBitmapTop = skiaGlyph.fTop; 537 glyph->mLsbDelta = skiaGlyph.fLsbDelta; 538 glyph->mRsbDelta = skiaGlyph.fRsbDelta; 539 540 uint32_t startX = 0; 541 uint32_t startY = 0; 542 543 // Get the bitmap for the glyph 544 paint->findImage(skiaGlyph); 545 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY); 546 547 if (!glyph->mIsValid) { 548 return; 549 } 550 551 uint32_t endX = startX + skiaGlyph.fWidth; 552 uint32_t endY = startY + skiaGlyph.fHeight; 553 554 glyph->mStartX = startX; 555 glyph->mStartY = startY; 556 glyph->mBitmapWidth = skiaGlyph.fWidth; 557 glyph->mBitmapHeight = skiaGlyph.fHeight; 558 559 uint32_t cacheWidth = glyph->mCacheTexture->mWidth; 560 uint32_t cacheHeight = glyph->mCacheTexture->mHeight; 561 562 glyph->mBitmapMinU = startX / (float) cacheWidth; 563 glyph->mBitmapMinV = startY / (float) cacheHeight; 564 glyph->mBitmapMaxU = endX / (float) cacheWidth; 565 glyph->mBitmapMaxV = endY / (float) cacheHeight; 566 567 mState->mUploadTexture = true; 568} 569 570CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { 571 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 572 mCachedGlyphs.add(glyph, newGlyph); 573 574 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph); 575 newGlyph->mGlyphIndex = skiaGlyph.fID; 576 newGlyph->mIsValid = false; 577 578 updateGlyphCache(paint, skiaGlyph, newGlyph); 579 580 return newGlyph; 581} 582 583Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize, 584 int flags, uint32_t italicStyle, uint32_t scaleX, 585 SkPaint::Style style, uint32_t strokeWidth) { 586 Vector<Font*> &activeFonts = state->mActiveFonts; 587 588 for (uint32_t i = 0; i < activeFonts.size(); i++) { 589 Font* font = activeFonts[i]; 590 if (font->mFontId == fontId && font->mFontSize == fontSize && 591 font->mFlags == flags && font->mItalicStyle == italicStyle && 592 font->mScaleX == scaleX && font->mStyle == style && 593 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) { 594 return font; 595 } 596 } 597 598 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, 599 scaleX, style, strokeWidth); 600 activeFonts.push(newFont); 601 return newFont; 602} 603 604/////////////////////////////////////////////////////////////////////////////// 605// FontRenderer 606/////////////////////////////////////////////////////////////////////////////// 607 608static bool sLogFontRendererCreate = true; 609 610FontRenderer::FontRenderer() { 611 if (sLogFontRendererCreate) { 612 INIT_LOGD("Creating FontRenderer"); 613 } 614 615 mGammaTable = NULL; 616 mInitialized = false; 617 mMaxNumberOfQuads = 1024; 618 mCurrentQuadIndex = 0; 619 620 mTextMeshPtr = NULL; 621 mCurrentCacheTexture = NULL; 622 mLastCacheTexture = NULL; 623 624 mLinearFiltering = false; 625 626 mIndexBufferID = 0; 627 628 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 629 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 630 631 char property[PROPERTY_VALUE_MAX]; 632 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 633 if (sLogFontRendererCreate) { 634 INIT_LOGD(" Setting text cache width to %s pixels", property); 635 } 636 mSmallCacheWidth = atoi(property); 637 } else { 638 if (sLogFontRendererCreate) { 639 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth); 640 } 641 } 642 643 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 644 if (sLogFontRendererCreate) { 645 INIT_LOGD(" Setting text cache width to %s pixels", property); 646 } 647 mSmallCacheHeight = atoi(property); 648 } else { 649 if (sLogFontRendererCreate) { 650 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight); 651 } 652 } 653 654 sLogFontRendererCreate = false; 655} 656 657FontRenderer::~FontRenderer() { 658 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 659 delete mCacheTextures[i]; 660 } 661 mCacheTextures.clear(); 662 663 if (mInitialized) { 664 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers 665 Caches::getInstance().unbindIndicesBuffer(); 666 glDeleteBuffers(1, &mIndexBufferID); 667 668 delete[] mTextMeshPtr; 669 } 670 671 Vector<Font*> fontsToDereference = mActiveFonts; 672 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 673 delete fontsToDereference[i]; 674 } 675} 676 677void FontRenderer::flushAllAndInvalidate() { 678 if (mCurrentQuadIndex != 0) { 679 issueDrawCommand(); 680 mCurrentQuadIndex = 0; 681 } 682 683 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 684 mActiveFonts[i]->invalidateTextureCache(); 685 } 686 687 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 688 mCacheTextures[i]->init(); 689 } 690 691 #if DEBUG_FONT_RENDERER 692 uint16_t totalGlyphs = 0; 693 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 694 totalGlyphs += mCacheTextures[i]->mNumGlyphs; 695 // Erase caches, just as a debugging facility 696 if (mCacheTextures[i]->mTexture) { 697 memset(mCacheTextures[i]->mTexture, 0, 698 mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight); 699 } 700 } 701 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs); 702#endif 703} 704 705void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) { 706 if (cacheTexture && cacheTexture->mTexture) { 707 glDeleteTextures(1, &cacheTexture->mTextureId); 708 delete[] cacheTexture->mTexture; 709 cacheTexture->mTexture = NULL; 710 cacheTexture->mTextureId = 0; 711 } 712} 713 714void FontRenderer::flushLargeCaches() { 715 // Start from 1; don't deallocate smallest/default texture 716 for (uint32_t i = 1; i < mCacheTextures.size(); i++) { 717 CacheTexture* cacheTexture = mCacheTextures[i]; 718 if (cacheTexture->mTexture != NULL) { 719 cacheTexture->init(); 720 for (uint32_t j = 0; j < mActiveFonts.size(); j++) { 721 mActiveFonts[j]->invalidateTextureCache(cacheTexture); 722 } 723 deallocateTextureMemory(cacheTexture); 724 } 725 } 726} 727 728void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) { 729 int width = cacheTexture->mWidth; 730 int height = cacheTexture->mHeight; 731 732 cacheTexture->mTexture = new uint8_t[width * height]; 733 734 if (!cacheTexture->mTextureId) { 735 glGenTextures(1, &cacheTexture->mTextureId); 736 } 737 738 Caches::getInstance().activeTexture(0); 739 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 740 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 741 // Initialize texture dimensions 742 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, 743 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 744 745 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST; 746 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 747 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 748 749 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 751} 752 753CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph, 754 uint32_t* startX, uint32_t* startY) { 755 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 756 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) { 757 return mCacheTextures[i]; 758 } 759 } 760 // Could not fit glyph into current cache textures 761 return NULL; 762} 763 764void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 765 uint32_t* retOriginX, uint32_t* retOriginY) { 766 checkInit(); 767 cachedGlyph->mIsValid = false; 768 // If the glyph is too tall, don't cache it 769 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > 770 mCacheTextures[mCacheTextures.size() - 1]->mHeight) { 771 ALOGE("Font size too large to fit in cache. width, height = %i, %i", 772 (int) glyph.fWidth, (int) glyph.fHeight); 773 return; 774 } 775 776 // Now copy the bitmap into the cache texture 777 uint32_t startX = 0; 778 uint32_t startY = 0; 779 780 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 781 782 // If the new glyph didn't fit, flush the state so far and invalidate everything 783 if (!cacheTexture) { 784 flushAllAndInvalidate(); 785 786 // Try to fit it again 787 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 788 789 // if we still don't fit, something is wrong and we shouldn't draw 790 if (!cacheTexture) { 791 return; 792 } 793 } 794 795 cachedGlyph->mCacheTexture = cacheTexture; 796 797 *retOriginX = startX; 798 *retOriginY = startY; 799 800 uint32_t endX = startX + glyph.fWidth; 801 uint32_t endY = startY + glyph.fHeight; 802 803 uint32_t cacheWidth = cacheTexture->mWidth; 804 805 if (!cacheTexture->mTexture) { 806 // Large-glyph texture memory is allocated only as needed 807 allocateTextureMemory(cacheTexture); 808 } 809 810 uint8_t* cacheBuffer = cacheTexture->mTexture; 811 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 812 unsigned int stride = glyph.rowBytes(); 813 814 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 815 816 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) { 817 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0; 818 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0; 819 } 820 821 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1; 822 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) { 823 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0; 824 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0; 825 } 826 827 if (mGammaTable) { 828 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 829 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 830 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 831 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 832 } 833 } 834 } else { 835 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 836 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 837 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 838 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol; 839 } 840 } 841 } 842 843 cachedGlyph->mIsValid = true; 844} 845 846CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 847 CacheTexture* cacheTexture = new CacheTexture(width, height); 848 849 if (allocate) { 850 allocateTextureMemory(cacheTexture); 851 } 852 853 return cacheTexture; 854} 855 856void FontRenderer::initTextTexture() { 857 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 858 delete mCacheTextures[i]; 859 } 860 mCacheTextures.clear(); 861 862 // Next, use other, separate caches for large glyphs. 863 uint16_t maxWidth = 0; 864 if (Caches::hasInstance()) { 865 maxWidth = Caches::getInstance().maxTextureSize; 866 } 867 868 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) { 869 maxWidth = MAX_TEXT_CACHE_WIDTH; 870 } 871 872 mUploadTexture = false; 873 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true)); 874 mCacheTextures.push(createCacheTexture(maxWidth, 256, false)); 875 mCacheTextures.push(createCacheTexture(maxWidth, 256, false)); 876 mCacheTextures.push(createCacheTexture(maxWidth, 512, false)); 877 mCurrentCacheTexture = mCacheTextures[0]; 878} 879 880// Avoid having to reallocate memory and render quad by quad 881void FontRenderer::initVertexArrayBuffers() { 882 uint32_t numIndices = mMaxNumberOfQuads * 6; 883 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 884 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 885 886 // Four verts, two triangles , six indices per quad 887 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 888 int i6 = i * 6; 889 int i4 = i * 4; 890 891 indexBufferData[i6 + 0] = i4 + 0; 892 indexBufferData[i6 + 1] = i4 + 1; 893 indexBufferData[i6 + 2] = i4 + 2; 894 895 indexBufferData[i6 + 3] = i4 + 0; 896 indexBufferData[i6 + 4] = i4 + 2; 897 indexBufferData[i6 + 5] = i4 + 3; 898 } 899 900 glGenBuffers(1, &mIndexBufferID); 901 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 902 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 903 904 free(indexBufferData); 905 906 uint32_t coordSize = 2; 907 uint32_t uvSize = 2; 908 uint32_t vertsPerQuad = 4; 909 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 910 mTextMeshPtr = new float[vertexBufferSize]; 911} 912 913// We don't want to allocate anything unless we actually draw text 914void FontRenderer::checkInit() { 915 if (mInitialized) { 916 return; 917 } 918 919 initTextTexture(); 920 initVertexArrayBuffers(); 921 922 mInitialized = true; 923} 924 925void FontRenderer::checkTextureUpdate() { 926 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) { 927 return; 928 } 929 930 Caches& caches = Caches::getInstance(); 931 GLuint lastTextureId = 0; 932 // Iterate over all the cache textures and see which ones need to be updated 933 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 934 CacheTexture* cacheTexture = mCacheTextures[i]; 935 if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) { 936 uint32_t xOffset = 0; 937 uint32_t width = cacheTexture->mWidth; 938 uint32_t height = cacheTexture->mHeight; 939 void* textureData = cacheTexture->mTexture; 940 941 if (cacheTexture->mTextureId != lastTextureId) { 942 caches.activeTexture(0); 943 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 944 lastTextureId = cacheTexture->mTextureId; 945 } 946#if DEBUG_FONT_RENDERER 947 ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d", 948 i, xOffset, width, height); 949#endif 950 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height, 951 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 952 953 cacheTexture->mDirty = false; 954 } 955 } 956 957 caches.activeTexture(0); 958 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); 959 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) { 960 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST; 961 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 962 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 963 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering; 964 } 965 mLastCacheTexture = mCurrentCacheTexture; 966 967 mUploadTexture = false; 968} 969 970void FontRenderer::issueDrawCommand() { 971 checkTextureUpdate(); 972 973 Caches& caches = Caches::getInstance(); 974 caches.bindIndicesBuffer(mIndexBufferID); 975 if (!mDrawn) { 976 float* buffer = mTextMeshPtr; 977 int offset = 2; 978 979 bool force = caches.unbindMeshBuffer(); 980 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 981 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 982 buffer + offset); 983 } 984 985 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 986 987 mDrawn = true; 988} 989 990void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 991 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 992 float x4, float y4, float u4, float v4, CacheTexture* texture) { 993 if (texture != mCurrentCacheTexture) { 994 if (mCurrentQuadIndex != 0) { 995 // First, draw everything stored already which uses the previous texture 996 issueDrawCommand(); 997 mCurrentQuadIndex = 0; 998 } 999 // Now use the new texture id 1000 mCurrentCacheTexture = texture; 1001 } 1002 1003 const uint32_t vertsPerQuad = 4; 1004 const uint32_t floatsPerVert = 4; 1005 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 1006 1007 (*currentPos++) = x1; 1008 (*currentPos++) = y1; 1009 (*currentPos++) = u1; 1010 (*currentPos++) = v1; 1011 1012 (*currentPos++) = x2; 1013 (*currentPos++) = y2; 1014 (*currentPos++) = u2; 1015 (*currentPos++) = v2; 1016 1017 (*currentPos++) = x3; 1018 (*currentPos++) = y3; 1019 (*currentPos++) = u3; 1020 (*currentPos++) = v3; 1021 1022 (*currentPos++) = x4; 1023 (*currentPos++) = y4; 1024 (*currentPos++) = u4; 1025 (*currentPos++) = v4; 1026 1027 mCurrentQuadIndex++; 1028} 1029 1030void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 1031 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 1032 float x4, float y4, float u4, float v4, CacheTexture* texture) { 1033 1034 if (mClip && 1035 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 1036 return; 1037 } 1038 1039 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 1040 1041 if (mBounds) { 1042 mBounds->left = fmin(mBounds->left, x1); 1043 mBounds->top = fmin(mBounds->top, y3); 1044 mBounds->right = fmax(mBounds->right, x3); 1045 mBounds->bottom = fmax(mBounds->bottom, y1); 1046 } 1047 1048 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 1049 issueDrawCommand(); 1050 mCurrentQuadIndex = 0; 1051 } 1052} 1053 1054void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 1055 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 1056 float x4, float y4, float u4, float v4, CacheTexture* texture) { 1057 1058 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 1059 1060 if (mBounds) { 1061 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 1062 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 1063 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 1064 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 1065 } 1066 1067 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 1068 issueDrawCommand(); 1069 mCurrentQuadIndex = 0; 1070 } 1071} 1072 1073void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 1074 int flags = 0; 1075 if (paint->isFakeBoldText()) { 1076 flags |= Font::kFakeBold; 1077 } 1078 1079 const float skewX = paint->getTextSkewX(); 1080 uint32_t italicStyle = *(uint32_t*) &skewX; 1081 const float scaleXFloat = paint->getTextScaleX(); 1082 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 1083 SkPaint::Style style = paint->getStyle(); 1084 const float strokeWidthFloat = paint->getStrokeWidth(); 1085 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 1086 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 1087 scaleX, style, strokeWidth); 1088 1089} 1090 1091FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 1092 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 1093 checkInit(); 1094 1095 if (!mCurrentFont) { 1096 DropShadow image; 1097 image.width = 0; 1098 image.height = 0; 1099 image.image = NULL; 1100 image.penX = 0; 1101 image.penY = 0; 1102 return image; 1103 } 1104 1105 mDrawn = false; 1106 mClip = NULL; 1107 mBounds = NULL; 1108 1109 Rect bounds; 1110 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 1111 1112 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 1113 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 1114 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 1115 1116 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 1117 dataBuffer[i] = 0; 1118 } 1119 1120 int penX = radius - bounds.left; 1121 int penY = radius - bounds.bottom; 1122 1123 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 1124 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 1125 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 1126 1127 DropShadow image; 1128 image.width = paddedWidth; 1129 image.height = paddedHeight; 1130 image.image = dataBuffer; 1131 image.penX = penX; 1132 image.penY = penY; 1133 1134 return image; 1135} 1136 1137void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 1138 checkInit(); 1139 1140 mDrawn = false; 1141 mBounds = bounds; 1142 mClip = clip; 1143} 1144 1145void FontRenderer::finishRender() { 1146 mBounds = NULL; 1147 mClip = NULL; 1148 1149 if (mCurrentQuadIndex != 0) { 1150 issueDrawCommand(); 1151 mCurrentQuadIndex = 0; 1152 } 1153} 1154 1155void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) { 1156 int flags = 0; 1157 if (paint->isFakeBoldText()) { 1158 flags |= Font::kFakeBold; 1159 } 1160 const float skewX = paint->getTextSkewX(); 1161 uint32_t italicStyle = *(uint32_t*) &skewX; 1162 const float scaleXFloat = paint->getTextScaleX(); 1163 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 1164 SkPaint::Style style = paint->getStyle(); 1165 const float strokeWidthFloat = paint->getStrokeWidth(); 1166 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 1167 float fontSize = paint->getTextSize(); 1168 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()), 1169 fontSize, flags, italicStyle, scaleX, style, strokeWidth); 1170 1171 font->precache(paint, text, numGlyphs); 1172} 1173 1174bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 1175 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 1176 if (!mCurrentFont) { 1177 ALOGE("No font set"); 1178 return false; 1179 } 1180 1181 initRender(clip, bounds); 1182 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 1183 finishRender(); 1184 1185 return mDrawn; 1186} 1187 1188bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 1189 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 1190 const float* positions, Rect* bounds) { 1191 if (!mCurrentFont) { 1192 ALOGE("No font set"); 1193 return false; 1194 } 1195 1196 initRender(clip, bounds); 1197 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 1198 finishRender(); 1199 1200 return mDrawn; 1201} 1202 1203bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 1204 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 1205 float hOffset, float vOffset, Rect* bounds) { 1206 if (!mCurrentFont) { 1207 ALOGE("No font set"); 1208 return false; 1209 } 1210 1211 initRender(clip, bounds); 1212 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 1213 finishRender(); 1214 1215 return mDrawn; 1216} 1217 1218void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 1219 // Compute gaussian weights for the blur 1220 // e is the euler's number 1221 float e = 2.718281828459045f; 1222 float pi = 3.1415926535897932f; 1223 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 1224 // x is of the form [-radius .. 0 .. radius] 1225 // and sigma varies with radius. 1226 // Based on some experimental radius values and sigma's 1227 // we approximately fit sigma = f(radius) as 1228 // sigma = radius * 0.3 + 0.6 1229 // The larger the radius gets, the more our gaussian blur 1230 // will resemble a box blur since with large sigma 1231 // the gaussian curve begins to lose its shape 1232 float sigma = 0.3f * (float) radius + 0.6f; 1233 1234 // Now compute the coefficints 1235 // We will store some redundant values to save some math during 1236 // the blur calculations 1237 // precompute some values 1238 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 1239 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 1240 1241 float normalizeFactor = 0.0f; 1242 for (int32_t r = -radius; r <= radius; r ++) { 1243 float floatR = (float) r; 1244 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 1245 normalizeFactor += weights[r + radius]; 1246 } 1247 1248 //Now we need to normalize the weights because all our coefficients need to add up to one 1249 normalizeFactor = 1.0f / normalizeFactor; 1250 for (int32_t r = -radius; r <= radius; r ++) { 1251 weights[r + radius] *= normalizeFactor; 1252 } 1253} 1254 1255void FontRenderer::horizontalBlur(float* weights, int32_t radius, 1256 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1257 float blurredPixel = 0.0f; 1258 float currentPixel = 0.0f; 1259 1260 for (int32_t y = 0; y < height; y ++) { 1261 1262 const uint8_t* input = source + y * width; 1263 uint8_t* output = dest + y * width; 1264 1265 for (int32_t x = 0; x < width; x ++) { 1266 blurredPixel = 0.0f; 1267 const float* gPtr = weights; 1268 // Optimization for non-border pixels 1269 if (x > radius && x < (width - radius)) { 1270 const uint8_t *i = input + (x - radius); 1271 for (int r = -radius; r <= radius; r ++) { 1272 currentPixel = (float) (*i); 1273 blurredPixel += currentPixel * gPtr[0]; 1274 gPtr++; 1275 i++; 1276 } 1277 } else { 1278 for (int32_t r = -radius; r <= radius; r ++) { 1279 // Stepping left and right away from the pixel 1280 int validW = x + r; 1281 if (validW < 0) { 1282 validW = 0; 1283 } 1284 if (validW > width - 1) { 1285 validW = width - 1; 1286 } 1287 1288 currentPixel = (float) input[validW]; 1289 blurredPixel += currentPixel * gPtr[0]; 1290 gPtr++; 1291 } 1292 } 1293 *output = (uint8_t)blurredPixel; 1294 output ++; 1295 } 1296 } 1297} 1298 1299void FontRenderer::verticalBlur(float* weights, int32_t radius, 1300 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1301 float blurredPixel = 0.0f; 1302 float currentPixel = 0.0f; 1303 1304 for (int32_t y = 0; y < height; y ++) { 1305 1306 uint8_t* output = dest + y * width; 1307 1308 for (int32_t x = 0; x < width; x ++) { 1309 blurredPixel = 0.0f; 1310 const float* gPtr = weights; 1311 const uint8_t* input = source + x; 1312 // Optimization for non-border pixels 1313 if (y > radius && y < (height - radius)) { 1314 const uint8_t *i = input + ((y - radius) * width); 1315 for (int32_t r = -radius; r <= radius; r ++) { 1316 currentPixel = (float)(*i); 1317 blurredPixel += currentPixel * gPtr[0]; 1318 gPtr++; 1319 i += width; 1320 } 1321 } else { 1322 for (int32_t r = -radius; r <= radius; r ++) { 1323 int validH = y + r; 1324 // Clamp to zero and width 1325 if (validH < 0) { 1326 validH = 0; 1327 } 1328 if (validH > height - 1) { 1329 validH = height - 1; 1330 } 1331 1332 const uint8_t *i = input + validH * width; 1333 currentPixel = (float) (*i); 1334 blurredPixel += currentPixel * gPtr[0]; 1335 gPtr++; 1336 } 1337 } 1338 *output = (uint8_t) blurredPixel; 1339 output ++; 1340 } 1341 } 1342} 1343 1344 1345void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 1346 float *gaussian = new float[2 * radius + 1]; 1347 computeGaussianWeights(gaussian, radius); 1348 1349 uint8_t* scratch = new uint8_t[width * height]; 1350 1351 horizontalBlur(gaussian, radius, image, scratch, width, height); 1352 verticalBlur(gaussian, radius, scratch, image, width, height); 1353 1354 delete[] gaussian; 1355 delete[] scratch; 1356} 1357 1358}; // namespace uirenderer 1359}; // namespace android 1360