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