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