FontRenderer.cpp revision e816baea651476aca4407200d4a5e629b9ab8dfa
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// CacheTextureLine 113/////////////////////////////////////////////////////////////////////////////// 114 115bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { 116 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) { 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 = mCurrentRow + 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 (mMaxHeight - glyphH >= glyphH) { 150 // There's enough height left over to create a new CacheBlock 151 CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW, 152 mMaxHeight - 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(CacheTextureLine *cacheLine) { 217 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 218 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i); 219 if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) { 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->mCachedTextureLine->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->mCachedTextureLine->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->mCachedTextureLine->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->mCachedTextureLine->mCacheTexture->mWidth; 560 uint32_t cacheHeight = glyph->mCachedTextureLine->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 mCacheTextureSmall = NULL; 624 mCacheTexture128 = NULL; 625 mCacheTexture256 = NULL; 626 mCacheTexture512 = NULL; 627 628 mLinearFiltering = false; 629 630 mIndexBufferID = 0; 631 632 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 633 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 634 635 char property[PROPERTY_VALUE_MAX]; 636 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 637 if (sLogFontRendererCreate) { 638 INIT_LOGD(" Setting text cache width to %s pixels", property); 639 } 640 mSmallCacheWidth = atoi(property); 641 } else { 642 if (sLogFontRendererCreate) { 643 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth); 644 } 645 } 646 647 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 648 if (sLogFontRendererCreate) { 649 INIT_LOGD(" Setting text cache width to %s pixels", property); 650 } 651 mSmallCacheHeight = atoi(property); 652 } else { 653 if (sLogFontRendererCreate) { 654 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight); 655 } 656 } 657 658 sLogFontRendererCreate = false; 659} 660 661FontRenderer::~FontRenderer() { 662 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 663 delete mCacheLines[i]; 664 } 665 mCacheLines.clear(); 666 667 if (mInitialized) { 668 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers 669 Caches::getInstance().unbindIndicesBuffer(); 670 glDeleteBuffers(1, &mIndexBufferID); 671 672 delete[] mTextMeshPtr; 673 delete mCacheTextureSmall; 674 delete mCacheTexture128; 675 delete mCacheTexture256; 676 delete mCacheTexture512; 677 } 678 679 Vector<Font*> fontsToDereference = mActiveFonts; 680 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 681 delete fontsToDereference[i]; 682 } 683} 684 685void FontRenderer::flushAllAndInvalidate() { 686 if (mCurrentQuadIndex != 0) { 687 issueDrawCommand(); 688 mCurrentQuadIndex = 0; 689 } 690 691 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 692 mActiveFonts[i]->invalidateTextureCache(); 693 } 694 695 uint16_t totalGlyphs = 0; 696 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 697 totalGlyphs += mCacheLines[i]->mNumGlyphs; 698 mCacheLines[i]->init(); 699 } 700 701#if DEBUG_FONT_RENDERER 702 ALOGD("FontRenderer: flushAllAndInvalidatel"); 703 // Erase caches, just as a debugging facility 704 if (mCacheTextureSmall && mCacheTextureSmall->mTexture) { 705 memset(mCacheTextureSmall->mTexture, 0, 706 mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight); 707 } 708 if (mCacheTexture128 && mCacheTexture128->mTexture) { 709 memset(mCacheTexture128->mTexture, 0, 710 mCacheTexture128->mWidth * mCacheTexture128->mHeight); 711 } 712 if (mCacheTexture256 && mCacheTexture256->mTexture) { 713 memset(mCacheTexture256->mTexture, 0, 714 mCacheTexture256->mWidth * mCacheTexture256->mHeight); 715 } 716 if (mCacheTexture512 && mCacheTexture512->mTexture) { 717 memset(mCacheTexture512->mTexture, 0, 718 mCacheTexture512->mWidth * mCacheTexture512->mHeight); 719 } 720 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs); 721#endif 722} 723 724void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) { 725 if (cacheTexture && cacheTexture->mTexture) { 726 glDeleteTextures(1, &cacheTexture->mTextureId); 727 delete[] cacheTexture->mTexture; 728 cacheTexture->mTexture = NULL; 729 cacheTexture->mTextureId = 0; 730 } 731} 732 733void FontRenderer::flushLargeCaches() { 734 if ((!mCacheTexture128 || !mCacheTexture128->mTexture) && 735 (!mCacheTexture256 || !mCacheTexture256->mTexture) && 736 (!mCacheTexture512 || !mCacheTexture512->mTexture)) { 737 // Typical case; no large glyph caches allocated 738 return; 739 } 740 741 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 742 CacheTextureLine* cacheLine = mCacheLines[i]; 743 if ((cacheLine->mCacheTexture == mCacheTexture128 || 744 cacheLine->mCacheTexture == mCacheTexture256 || 745 cacheLine->mCacheTexture == mCacheTexture512) && 746 cacheLine->mCacheTexture->mTexture != NULL) { 747#if DEBUG_FONT_RENDERER 748 if (cacheLine->mCacheTexture == mCacheTexture128) { 749 ALOGD("flushing cacheTexture128"); 750 } else if (cacheLine->mCacheTexture == mCacheTexture256) { 751 ALOGD("flushing cacheTexture256"); 752 } else { 753 ALOGD("flushing cacheTexture512"); 754 } 755#endif 756 cacheLine->init(); 757 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 758 mActiveFonts[i]->invalidateTextureCache(cacheLine); 759 } 760 } 761 } 762 763 deallocateTextureMemory(mCacheTexture128); 764 deallocateTextureMemory(mCacheTexture256); 765 deallocateTextureMemory(mCacheTexture512); 766} 767 768void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) { 769 int width = cacheTexture->mWidth; 770 int height = cacheTexture->mHeight; 771 772 cacheTexture->mTexture = new uint8_t[width * height]; 773 774 if (!cacheTexture->mTextureId) { 775 glGenTextures(1, &cacheTexture->mTextureId); 776 } 777 778 Caches::getInstance().activeTexture(0); 779 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 780 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 781 // Initialize texture dimensions 782 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, 783 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 784 785 const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST; 786 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 787 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 788 789 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 790 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 791} 792 793void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 794 uint32_t* retOriginX, uint32_t* retOriginY) { 795 cachedGlyph->mIsValid = false; 796 // If the glyph is too tall, don't cache it 797 if (mCacheLines.size() == 0 || 798 glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 799 if (mCacheLines.size() != 0) { 800 ALOGE("Font size too large to fit in cache. width, height = %i, %i", 801 (int) glyph.fWidth, (int) glyph.fHeight); 802 } 803 return; 804 } 805 806 // Now copy the bitmap into the cache texture 807 uint32_t startX = 0; 808 uint32_t startY = 0; 809 810 bool bitmapFit = false; 811 CacheTextureLine *cacheLine; 812 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 813 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 814 if (bitmapFit) { 815 cacheLine = mCacheLines[i]; 816 break; 817 } 818 } 819 820 // If the new glyph didn't fit, flush the state so far and invalidate everything 821 if (!bitmapFit) { 822 flushAllAndInvalidate(); 823 824 // Try to fit it again 825 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 826 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 827 if (bitmapFit) { 828 cacheLine = mCacheLines[i]; 829 break; 830 } 831 } 832 833 // if we still don't fit, something is wrong and we shouldn't draw 834 if (!bitmapFit) { 835 return; 836 } 837 } 838 839 cachedGlyph->mCachedTextureLine = cacheLine; 840 841 *retOriginX = startX; 842 *retOriginY = startY; 843 844 uint32_t endX = startX + glyph.fWidth; 845 uint32_t endY = startY + glyph.fHeight; 846 847 uint32_t cacheWidth = cacheLine->mMaxWidth; 848 849 CacheTexture* cacheTexture = cacheLine->mCacheTexture; 850 if (!cacheTexture->mTexture) { 851 // Large-glyph texture memory is allocated only as needed 852 allocateTextureMemory(cacheTexture); 853 } 854 855 uint8_t* cacheBuffer = cacheTexture->mTexture; 856 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 857 unsigned int stride = glyph.rowBytes(); 858 859 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 860 861 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) { 862 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0; 863 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0; 864 } 865 866 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1; 867 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) { 868 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0; 869 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0; 870 } 871 872 if (mGammaTable) { 873 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 874 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 875 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 876 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 877 } 878 } 879 } else { 880 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 881 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 882 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 883 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol; 884 } 885 } 886 } 887 888 cachedGlyph->mIsValid = true; 889} 890 891CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 892 CacheTexture* cacheTexture = new CacheTexture(width, height); 893 894 if (allocate) { 895 allocateTextureMemory(cacheTexture); 896 } 897 898 return cacheTexture; 899} 900 901void FontRenderer::initTextTexture() { 902 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 903 delete mCacheLines[i]; 904 } 905 mCacheLines.clear(); 906 907 if (mCacheTextureSmall) { 908 delete mCacheTextureSmall; 909 delete mCacheTexture128; 910 delete mCacheTexture256; 911 delete mCacheTexture512; 912 } 913 914 // Next, use other, separate caches for large glyphs. 915 uint16_t maxWidth = 0; 916 if (Caches::hasInstance()) { 917 maxWidth = Caches::getInstance().maxTextureSize; 918 } 919 920 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) { 921 maxWidth = MAX_TEXT_CACHE_WIDTH; 922 } 923 924 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true); 925 mCacheTexture128 = createCacheTexture(maxWidth, 256, false); 926 mCacheTexture256 = createCacheTexture(maxWidth, 256, false); 927 mCacheTexture512 = createCacheTexture(maxWidth, 512, false); 928 mCurrentCacheTexture = mCacheTextureSmall; 929 930 mUploadTexture = false; 931 // Split up our default cache texture into lines of certain widths 932 int nextLine = 0; 933 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, mCacheTextureSmall)); 934 nextLine += mCacheLines.top()->mMaxHeight; 935 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall)); 936 nextLine += mCacheLines.top()->mMaxHeight; 937 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall)); 938 nextLine += mCacheLines.top()->mMaxHeight; 939 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall)); 940 nextLine += mCacheLines.top()->mMaxHeight; 941 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall)); 942 nextLine += mCacheLines.top()->mMaxHeight; 943 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, mCacheTextureSmall)); 944 nextLine += mCacheLines.top()->mMaxHeight; 945 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine, 946 nextLine, mCacheTextureSmall)); 947 948 // The first cache is split into 2 lines of height 128, the rest have just one cache line. 949 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, mCacheTexture128)); 950 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, mCacheTexture128)); 951 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, mCacheTexture256)); 952 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, mCacheTexture512)); 953} 954 955// Avoid having to reallocate memory and render quad by quad 956void FontRenderer::initVertexArrayBuffers() { 957 uint32_t numIndices = mMaxNumberOfQuads * 6; 958 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 959 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 960 961 // Four verts, two triangles , six indices per quad 962 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 963 int i6 = i * 6; 964 int i4 = i * 4; 965 966 indexBufferData[i6 + 0] = i4 + 0; 967 indexBufferData[i6 + 1] = i4 + 1; 968 indexBufferData[i6 + 2] = i4 + 2; 969 970 indexBufferData[i6 + 3] = i4 + 0; 971 indexBufferData[i6 + 4] = i4 + 2; 972 indexBufferData[i6 + 5] = i4 + 3; 973 } 974 975 glGenBuffers(1, &mIndexBufferID); 976 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 977 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 978 979 free(indexBufferData); 980 981 uint32_t coordSize = 2; 982 uint32_t uvSize = 2; 983 uint32_t vertsPerQuad = 4; 984 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 985 mTextMeshPtr = new float[vertexBufferSize]; 986} 987 988// We don't want to allocate anything unless we actually draw text 989void FontRenderer::checkInit() { 990 if (mInitialized) { 991 return; 992 } 993 994 initTextTexture(); 995 initVertexArrayBuffers(); 996 997 mInitialized = true; 998} 999 1000void FontRenderer::checkTextureUpdate() { 1001 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) { 1002 return; 1003 } 1004 1005 Caches& caches = Caches::getInstance(); 1006 GLuint lastTextureId = 0; 1007 // Iterate over all the cache lines and see which ones need to be updated 1008 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 1009 CacheTextureLine* cl = mCacheLines[i]; 1010 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) { 1011 CacheTexture* cacheTexture = cl->mCacheTexture; 1012 uint32_t xOffset = 0; 1013 uint32_t yOffset = cl->mCurrentRow; 1014 uint32_t width = cl->mMaxWidth; 1015 uint32_t height = cl->mMaxHeight; 1016 void* textureData = cacheTexture->mTexture + (yOffset * width); 1017 1018 if (cacheTexture->mTextureId != lastTextureId) { 1019 caches.activeTexture(0); 1020 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 1021 lastTextureId = cacheTexture->mTextureId; 1022 } 1023#if DEBUG_FONT_RENDERER 1024 ALOGD("glTextSubimage for cacheLine %d: xOff, yOff, width height = %d, %d, %d, %d", i, 1025 xOffset, yOffset, width, height); 1026#endif 1027 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 1028 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 1029 1030 cl->mDirty = false; 1031 } 1032 } 1033 1034 caches.activeTexture(0); 1035 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); 1036 if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) { 1037 const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST; 1038 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); 1039 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); 1040 mCurrentCacheTexture->mLinearFiltering = mLinearFiltering; 1041 } 1042 mLastCacheTexture = mCurrentCacheTexture; 1043 1044 mUploadTexture = false; 1045} 1046 1047void FontRenderer::issueDrawCommand() { 1048 checkTextureUpdate(); 1049 1050 Caches& caches = Caches::getInstance(); 1051 caches.bindIndicesBuffer(mIndexBufferID); 1052 if (!mDrawn) { 1053 float* buffer = mTextMeshPtr; 1054 int offset = 2; 1055 1056 bool force = caches.unbindMeshBuffer(); 1057 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 1058 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 1059 buffer + offset); 1060 } 1061 1062 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 1063 1064 mDrawn = true; 1065} 1066 1067void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 1068 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 1069 float x4, float y4, float u4, float v4, CacheTexture* texture) { 1070 if (texture != mCurrentCacheTexture) { 1071 if (mCurrentQuadIndex != 0) { 1072 // First, draw everything stored already which uses the previous texture 1073 issueDrawCommand(); 1074 mCurrentQuadIndex = 0; 1075 } 1076 // Now use the new texture id 1077 mCurrentCacheTexture = texture; 1078 } 1079 1080 const uint32_t vertsPerQuad = 4; 1081 const uint32_t floatsPerVert = 4; 1082 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 1083 1084 (*currentPos++) = x1; 1085 (*currentPos++) = y1; 1086 (*currentPos++) = u1; 1087 (*currentPos++) = v1; 1088 1089 (*currentPos++) = x2; 1090 (*currentPos++) = y2; 1091 (*currentPos++) = u2; 1092 (*currentPos++) = v2; 1093 1094 (*currentPos++) = x3; 1095 (*currentPos++) = y3; 1096 (*currentPos++) = u3; 1097 (*currentPos++) = v3; 1098 1099 (*currentPos++) = x4; 1100 (*currentPos++) = y4; 1101 (*currentPos++) = u4; 1102 (*currentPos++) = v4; 1103 1104 mCurrentQuadIndex++; 1105} 1106 1107void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 1108 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 1109 float x4, float y4, float u4, float v4, CacheTexture* texture) { 1110 1111 if (mClip && 1112 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 1113 return; 1114 } 1115 1116 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 1117 1118 if (mBounds) { 1119 mBounds->left = fmin(mBounds->left, x1); 1120 mBounds->top = fmin(mBounds->top, y3); 1121 mBounds->right = fmax(mBounds->right, x3); 1122 mBounds->bottom = fmax(mBounds->bottom, y1); 1123 } 1124 1125 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 1126 issueDrawCommand(); 1127 mCurrentQuadIndex = 0; 1128 } 1129} 1130 1131void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 1132 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 1133 float x4, float y4, float u4, float v4, CacheTexture* texture) { 1134 1135 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 1136 1137 if (mBounds) { 1138 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 1139 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 1140 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 1141 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 1142 } 1143 1144 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 1145 issueDrawCommand(); 1146 mCurrentQuadIndex = 0; 1147 } 1148} 1149 1150void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 1151 int flags = 0; 1152 if (paint->isFakeBoldText()) { 1153 flags |= Font::kFakeBold; 1154 } 1155 1156 const float skewX = paint->getTextSkewX(); 1157 uint32_t italicStyle = *(uint32_t*) &skewX; 1158 const float scaleXFloat = paint->getTextScaleX(); 1159 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 1160 SkPaint::Style style = paint->getStyle(); 1161 const float strokeWidthFloat = paint->getStrokeWidth(); 1162 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 1163 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 1164 scaleX, style, strokeWidth); 1165 1166} 1167 1168FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 1169 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 1170 checkInit(); 1171 1172 if (!mCurrentFont) { 1173 DropShadow image; 1174 image.width = 0; 1175 image.height = 0; 1176 image.image = NULL; 1177 image.penX = 0; 1178 image.penY = 0; 1179 return image; 1180 } 1181 1182 mDrawn = false; 1183 mClip = NULL; 1184 mBounds = NULL; 1185 1186 Rect bounds; 1187 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 1188 1189 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 1190 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 1191 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 1192 1193 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 1194 dataBuffer[i] = 0; 1195 } 1196 1197 int penX = radius - bounds.left; 1198 int penY = radius - bounds.bottom; 1199 1200 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 1201 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 1202 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 1203 1204 DropShadow image; 1205 image.width = paddedWidth; 1206 image.height = paddedHeight; 1207 image.image = dataBuffer; 1208 image.penX = penX; 1209 image.penY = penY; 1210 1211 return image; 1212} 1213 1214void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 1215 checkInit(); 1216 1217 mDrawn = false; 1218 mBounds = bounds; 1219 mClip = clip; 1220} 1221 1222void FontRenderer::finishRender() { 1223 mBounds = NULL; 1224 mClip = NULL; 1225 1226 if (mCurrentQuadIndex != 0) { 1227 issueDrawCommand(); 1228 mCurrentQuadIndex = 0; 1229 } 1230} 1231 1232void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) { 1233 int flags = 0; 1234 if (paint->isFakeBoldText()) { 1235 flags |= Font::kFakeBold; 1236 } 1237 const float skewX = paint->getTextSkewX(); 1238 uint32_t italicStyle = *(uint32_t*) &skewX; 1239 const float scaleXFloat = paint->getTextScaleX(); 1240 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 1241 SkPaint::Style style = paint->getStyle(); 1242 const float strokeWidthFloat = paint->getStrokeWidth(); 1243 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 1244 float fontSize = paint->getTextSize(); 1245 Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()), 1246 fontSize, flags, italicStyle, scaleX, style, strokeWidth); 1247 1248 font->precache(paint, text, numGlyphs); 1249} 1250 1251bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 1252 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 1253 if (!mCurrentFont) { 1254 ALOGE("No font set"); 1255 return false; 1256 } 1257 1258 initRender(clip, bounds); 1259 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 1260 finishRender(); 1261 1262 return mDrawn; 1263} 1264 1265bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 1266 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 1267 const float* positions, Rect* bounds) { 1268 if (!mCurrentFont) { 1269 ALOGE("No font set"); 1270 return false; 1271 } 1272 1273 initRender(clip, bounds); 1274 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 1275 finishRender(); 1276 1277 return mDrawn; 1278} 1279 1280bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 1281 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 1282 float hOffset, float vOffset, Rect* bounds) { 1283 if (!mCurrentFont) { 1284 ALOGE("No font set"); 1285 return false; 1286 } 1287 1288 initRender(clip, bounds); 1289 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 1290 finishRender(); 1291 1292 return mDrawn; 1293} 1294 1295void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 1296 // Compute gaussian weights for the blur 1297 // e is the euler's number 1298 float e = 2.718281828459045f; 1299 float pi = 3.1415926535897932f; 1300 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 1301 // x is of the form [-radius .. 0 .. radius] 1302 // and sigma varies with radius. 1303 // Based on some experimental radius values and sigma's 1304 // we approximately fit sigma = f(radius) as 1305 // sigma = radius * 0.3 + 0.6 1306 // The larger the radius gets, the more our gaussian blur 1307 // will resemble a box blur since with large sigma 1308 // the gaussian curve begins to lose its shape 1309 float sigma = 0.3f * (float) radius + 0.6f; 1310 1311 // Now compute the coefficints 1312 // We will store some redundant values to save some math during 1313 // the blur calculations 1314 // precompute some values 1315 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 1316 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 1317 1318 float normalizeFactor = 0.0f; 1319 for (int32_t r = -radius; r <= radius; r ++) { 1320 float floatR = (float) r; 1321 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 1322 normalizeFactor += weights[r + radius]; 1323 } 1324 1325 //Now we need to normalize the weights because all our coefficients need to add up to one 1326 normalizeFactor = 1.0f / normalizeFactor; 1327 for (int32_t r = -radius; r <= radius; r ++) { 1328 weights[r + radius] *= normalizeFactor; 1329 } 1330} 1331 1332void FontRenderer::horizontalBlur(float* weights, int32_t radius, 1333 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1334 float blurredPixel = 0.0f; 1335 float currentPixel = 0.0f; 1336 1337 for (int32_t y = 0; y < height; y ++) { 1338 1339 const uint8_t* input = source + y * width; 1340 uint8_t* output = dest + y * width; 1341 1342 for (int32_t x = 0; x < width; x ++) { 1343 blurredPixel = 0.0f; 1344 const float* gPtr = weights; 1345 // Optimization for non-border pixels 1346 if (x > radius && x < (width - radius)) { 1347 const uint8_t *i = input + (x - radius); 1348 for (int r = -radius; r <= radius; r ++) { 1349 currentPixel = (float) (*i); 1350 blurredPixel += currentPixel * gPtr[0]; 1351 gPtr++; 1352 i++; 1353 } 1354 } else { 1355 for (int32_t r = -radius; r <= radius; r ++) { 1356 // Stepping left and right away from the pixel 1357 int validW = x + r; 1358 if (validW < 0) { 1359 validW = 0; 1360 } 1361 if (validW > width - 1) { 1362 validW = width - 1; 1363 } 1364 1365 currentPixel = (float) input[validW]; 1366 blurredPixel += currentPixel * gPtr[0]; 1367 gPtr++; 1368 } 1369 } 1370 *output = (uint8_t)blurredPixel; 1371 output ++; 1372 } 1373 } 1374} 1375 1376void FontRenderer::verticalBlur(float* weights, int32_t radius, 1377 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 1378 float blurredPixel = 0.0f; 1379 float currentPixel = 0.0f; 1380 1381 for (int32_t y = 0; y < height; y ++) { 1382 1383 uint8_t* output = dest + y * width; 1384 1385 for (int32_t x = 0; x < width; x ++) { 1386 blurredPixel = 0.0f; 1387 const float* gPtr = weights; 1388 const uint8_t* input = source + x; 1389 // Optimization for non-border pixels 1390 if (y > radius && y < (height - radius)) { 1391 const uint8_t *i = input + ((y - radius) * width); 1392 for (int32_t r = -radius; r <= radius; r ++) { 1393 currentPixel = (float)(*i); 1394 blurredPixel += currentPixel * gPtr[0]; 1395 gPtr++; 1396 i += width; 1397 } 1398 } else { 1399 for (int32_t r = -radius; r <= radius; r ++) { 1400 int validH = y + r; 1401 // Clamp to zero and width 1402 if (validH < 0) { 1403 validH = 0; 1404 } 1405 if (validH > height - 1) { 1406 validH = height - 1; 1407 } 1408 1409 const uint8_t *i = input + validH * width; 1410 currentPixel = (float) (*i); 1411 blurredPixel += currentPixel * gPtr[0]; 1412 gPtr++; 1413 } 1414 } 1415 *output = (uint8_t) blurredPixel; 1416 output ++; 1417 } 1418 } 1419} 1420 1421 1422void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 1423 float *gaussian = new float[2 * radius + 1]; 1424 computeGaussianWeights(gaussian, radius); 1425 1426 uint8_t* scratch = new uint8_t[width * height]; 1427 1428 horizontalBlur(gaussian, radius, image, scratch, width, height); 1429 verticalBlur(gaussian, radius, scratch, image, width, height); 1430 1431 delete[] gaussian; 1432 delete[] scratch; 1433} 1434 1435}; // namespace uirenderer 1436}; // namespace android 1437