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