FontRenderer.cpp revision 58ef7fbf16864164efe98bf613b15c64deb1afc0
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#include <utils/Log.h> 23 24#include "FontRenderer.h" 25 26namespace android { 27namespace uirenderer { 28 29/////////////////////////////////////////////////////////////////////////////// 30// Defines 31/////////////////////////////////////////////////////////////////////////////// 32 33#define DEFAULT_TEXT_CACHE_WIDTH 1024 34#define DEFAULT_TEXT_CACHE_HEIGHT 256 35 36/////////////////////////////////////////////////////////////////////////////// 37// Font 38/////////////////////////////////////////////////////////////////////////////// 39 40Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) : 41 mState(state), mFontId(fontId), mFontSize(fontSize) { 42} 43 44 45Font::~Font() { 46 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { 47 if (mState->mActiveFonts[ct] == this) { 48 mState->mActiveFonts.removeAt(ct); 49 break; 50 } 51 } 52 53 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 54 CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i); 55 delete glyph; 56 } 57} 58 59void Font::invalidateTextureCache() { 60 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 61 mCachedGlyphs.valueAt(i)->mIsValid = false; 62 } 63} 64 65void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) { 66 int nPenX = x + glyph->mBitmapLeft; 67 int nPenY = y + glyph->mBitmapTop; 68 69 int width = (int) glyph->mBitmapWidth; 70 int height = (int) glyph->mBitmapHeight; 71 72 if (bounds->bottom > nPenY) { 73 bounds->bottom = nPenY; 74 } 75 if (bounds->left > nPenX) { 76 bounds->left = nPenX; 77 } 78 if (bounds->right < nPenX + width) { 79 bounds->right = nPenX + width; 80 } 81 if (bounds->top < nPenY + height) { 82 bounds->top = nPenY + height; 83 } 84} 85 86void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) { 87 int nPenX = x + glyph->mBitmapLeft; 88 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; 89 90 float u1 = glyph->mBitmapMinU; 91 float u2 = glyph->mBitmapMaxU; 92 float v1 = glyph->mBitmapMinV; 93 float v2 = glyph->mBitmapMaxV; 94 95 int width = (int) glyph->mBitmapWidth; 96 int height = (int) glyph->mBitmapHeight; 97 98 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2, 99 nPenX + width, nPenY, 0, u2, v2, 100 nPenX + width, nPenY - height, 0, u2, v1, 101 nPenX, nPenY - height, 0, u1, v1); 102} 103 104void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, 105 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) { 106 int nPenX = x + glyph->mBitmapLeft; 107 int nPenY = y + glyph->mBitmapTop; 108 109 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth; 110 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight; 111 112 uint32_t cacheWidth = mState->getCacheWidth(); 113 const uint8_t* cacheBuffer = mState->getTextTextureData(); 114 115 uint32_t cacheX = 0, cacheY = 0; 116 int32_t bX = 0, bY = 0; 117 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) { 118 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) { 119 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { 120 LOGE("Skipping invalid index"); 121 continue; 122 } 123 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; 124 bitmap[bY * bitmapW + bX] = tempCol; 125 } 126 } 127 128} 129 130Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) { 131 CachedGlyphInfo* cachedGlyph = NULL; 132 ssize_t index = mCachedGlyphs.indexOfKey(utfChar); 133 if (index >= 0) { 134 cachedGlyph = mCachedGlyphs.valueAt(index); 135 } else { 136 cachedGlyph = cacheGlyph(paint, utfChar); 137 } 138 139 // Is the glyph still in texture cache? 140 if (!cachedGlyph->mIsValid) { 141 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar); 142 updateGlyphCache(paint, skiaGlyph, cachedGlyph); 143 } 144 145 return cachedGlyph; 146} 147 148void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 149 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 150 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { 151 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, 152 bitmapW, bitmapH, NULL); 153 } else { 154 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL); 155 } 156 157} 158 159void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 160 int numGlyphs, Rect *bounds) { 161 if (bounds == NULL) { 162 LOGE("No return rectangle provided to measure text"); 163 return; 164 } 165 bounds->set(1e6, -1e6, -1e6, 1e6); 166 renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); 167} 168 169#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) 170 171void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 172 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, 173 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) { 174 if (numGlyphs == 0 || text == NULL || len == 0) { 175 return; 176 } 177 178 SkFixed penX = SkIntToFixed(x); 179 int penY = y; 180 int glyphsLeft = 1; 181 if (numGlyphs > 0) { 182 glyphsLeft = numGlyphs; 183 } 184 185 SkFixed prevRsbDelta = 0; 186 penX += SK_Fixed1 / 2; 187 188 text += start; 189 190 while (glyphsLeft > 0) { 191 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); 192 193 // Reached the end of the string 194 if (utfChar < 0) { 195 break; 196 } 197 198 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); 199 penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta); 200 prevRsbDelta = cachedGlyph->mRsbDelta; 201 202 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 203 if (cachedGlyph->mIsValid) { 204 switch(mode) { 205 case FRAMEBUFFER: 206 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY); 207 break; 208 case BITMAP: 209 drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH); 210 break; 211 case MEASURE: 212 measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds); 213 break; 214 } 215 } 216 217 penX += cachedGlyph->mAdvanceX; 218 219 // If we were given a specific number of glyphs, decrement 220 if (numGlyphs > 0) { 221 glyphsLeft--; 222 } 223 } 224} 225 226void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { 227 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 228 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 229 glyph->mBitmapLeft = skiaGlyph.fLeft; 230 glyph->mBitmapTop = skiaGlyph.fTop; 231 glyph->mLsbDelta = skiaGlyph.fLsbDelta; 232 glyph->mRsbDelta = skiaGlyph.fRsbDelta; 233 234 uint32_t startX = 0; 235 uint32_t startY = 0; 236 237 // Get the bitmap for the glyph 238 paint->findImage(skiaGlyph); 239 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY); 240 241 if (!glyph->mIsValid) { 242 return; 243 } 244 245 uint32_t endX = startX + skiaGlyph.fWidth; 246 uint32_t endY = startY + skiaGlyph.fHeight; 247 248 glyph->mStartX = startX; 249 glyph->mStartY = startY; 250 glyph->mBitmapWidth = skiaGlyph.fWidth; 251 glyph->mBitmapHeight = skiaGlyph.fHeight; 252 253 uint32_t cacheWidth = mState->getCacheWidth(); 254 uint32_t cacheHeight = mState->getCacheHeight(); 255 256 glyph->mBitmapMinU = (float) startX / (float) cacheWidth; 257 glyph->mBitmapMinV = (float) startY / (float) cacheHeight; 258 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; 259 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; 260 261 mState->mUploadTexture = true; 262} 263 264Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) { 265 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 266 mCachedGlyphs.add(glyph, newGlyph); 267 268 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); 269 newGlyph->mGlyphIndex = skiaGlyph.fID; 270 newGlyph->mIsValid = false; 271 272 updateGlyphCache(paint, skiaGlyph, newGlyph); 273 274 return newGlyph; 275} 276 277Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) { 278 Vector<Font*> &activeFonts = state->mActiveFonts; 279 280 for (uint32_t i = 0; i < activeFonts.size(); i++) { 281 Font* font = activeFonts[i]; 282 if (font->mFontId == fontId && font->mFontSize == fontSize) { 283 return font; 284 } 285 } 286 287 Font* newFont = new Font(state, fontId, fontSize); 288 activeFonts.push(newFont); 289 return newFont; 290} 291 292/////////////////////////////////////////////////////////////////////////////// 293// FontRenderer 294/////////////////////////////////////////////////////////////////////////////// 295 296FontRenderer::FontRenderer() { 297 LOGD("Creating FontRenderer"); 298 299 mGammaTable = NULL; 300 mInitialized = false; 301 mMaxNumberOfQuads = 1024; 302 mCurrentQuadIndex = 0; 303 mTextureId = 0; 304 305 mTextMeshPtr = NULL; 306 mTextTexture = NULL; 307 308 mIndexBufferID = 0; 309 310 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 311 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 312 313 char property[PROPERTY_VALUE_MAX]; 314 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 315 LOGD(" Setting text cache width to %s pixels", property); 316 mCacheWidth = atoi(property); 317 } else { 318 LOGD(" Using default text cache width of %i pixels", mCacheWidth); 319 } 320 321 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 322 LOGD(" Setting text cache width to %s pixels", property); 323 mCacheHeight = atoi(property); 324 } else { 325 LOGD(" Using default text cache height of %i pixels", mCacheHeight); 326 } 327} 328 329FontRenderer::~FontRenderer() { 330 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 331 delete mCacheLines[i]; 332 } 333 mCacheLines.clear(); 334 335 if (mInitialized) { 336 delete[] mTextMeshPtr; 337 delete[] mTextTexture; 338 } 339 340 if (mTextureId) { 341 glDeleteTextures(1, &mTextureId); 342 } 343 344 Vector<Font*> fontsToDereference = mActiveFonts; 345 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 346 delete fontsToDereference[i]; 347 } 348} 349 350void FontRenderer::flushAllAndInvalidate() { 351 if (mCurrentQuadIndex != 0) { 352 issueDrawCommand(); 353 mCurrentQuadIndex = 0; 354 } 355 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 356 mActiveFonts[i]->invalidateTextureCache(); 357 } 358 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 359 mCacheLines[i]->mCurrentCol = 0; 360 } 361} 362 363bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { 364 // If the glyph is too tall, don't cache it 365 if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 366 LOGE("Font size to large to fit in cache. width, height = %i, %i", 367 (int) glyph.fWidth, (int) glyph.fHeight); 368 return false; 369 } 370 371 // Now copy the bitmap into the cache texture 372 uint32_t startX = 0; 373 uint32_t startY = 0; 374 375 bool bitmapFit = false; 376 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 377 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 378 if (bitmapFit) { 379 break; 380 } 381 } 382 383 // If the new glyph didn't fit, flush the state so far and invalidate everything 384 if (!bitmapFit) { 385 flushAllAndInvalidate(); 386 387 // Try to fit it again 388 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 389 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 390 if (bitmapFit) { 391 break; 392 } 393 } 394 395 // if we still don't fit, something is wrong and we shouldn't draw 396 if (!bitmapFit) { 397 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", 398 (int) glyph.fWidth, (int) glyph.fHeight); 399 return false; 400 } 401 } 402 403 *retOriginX = startX; 404 *retOriginY = startY; 405 406 uint32_t endX = startX + glyph.fWidth; 407 uint32_t endY = startY + glyph.fHeight; 408 409 uint32_t cacheWidth = mCacheWidth; 410 411 uint8_t* cacheBuffer = mTextTexture; 412 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 413 unsigned int stride = glyph.rowBytes(); 414 415 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 416 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 417 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 418 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 419 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 420 } 421 } 422 423 return true; 424} 425 426void FontRenderer::initTextTexture() { 427 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight]; 428 mUploadTexture = false; 429 430 glGenTextures(1, &mTextureId); 431 glBindTexture(GL_TEXTURE_2D, mTextureId); 432 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 433 // Initialize texture dimentions 434 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, 435 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 436 437 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 438 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 439 440 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 441 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 442 443 // Split up our cache texture into lines of certain widths 444 int nextLine = 0; 445 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0)); 446 nextLine += mCacheLines.top()->mMaxHeight; 447 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 448 nextLine += mCacheLines.top()->mMaxHeight; 449 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 450 nextLine += mCacheLines.top()->mMaxHeight; 451 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 452 nextLine += mCacheLines.top()->mMaxHeight; 453 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 454 nextLine += mCacheLines.top()->mMaxHeight; 455 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0)); 456 nextLine += mCacheLines.top()->mMaxHeight; 457 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); 458} 459 460// Avoid having to reallocate memory and render quad by quad 461void FontRenderer::initVertexArrayBuffers() { 462 uint32_t numIndicies = mMaxNumberOfQuads * 6; 463 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); 464 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 465 466 // Four verts, two triangles , six indices per quad 467 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 468 int i6 = i * 6; 469 int i4 = i * 4; 470 471 indexBufferData[i6 + 0] = i4 + 0; 472 indexBufferData[i6 + 1] = i4 + 1; 473 indexBufferData[i6 + 2] = i4 + 2; 474 475 indexBufferData[i6 + 3] = i4 + 0; 476 indexBufferData[i6 + 4] = i4 + 2; 477 indexBufferData[i6 + 5] = i4 + 3; 478 } 479 480 glGenBuffers(1, &mIndexBufferID); 481 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID); 482 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW); 483 glBindBuffer(GL_ARRAY_BUFFER, 0); 484 485 free(indexBufferData); 486 487 uint32_t coordSize = 3; 488 uint32_t uvSize = 2; 489 uint32_t vertsPerQuad = 4; 490 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 491 mTextMeshPtr = new float[vertexBufferSize]; 492} 493 494// We don't want to allocate anything unless we actually draw text 495void FontRenderer::checkInit() { 496 if (mInitialized) { 497 return; 498 } 499 500 initTextTexture(); 501 initVertexArrayBuffers(); 502 503 // We store a string with letters in a rough frequency of occurrence 504 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); 505 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); 506 mLatinPrecache += String16(",.?!()-+@;:`'"); 507 mLatinPrecache += String16("0123456789"); 508 509 mInitialized = true; 510} 511 512void FontRenderer::checkTextureUpdate() { 513 if (!mUploadTexture) { 514 return; 515 } 516 517 glBindTexture(GL_TEXTURE_2D, mTextureId); 518 519 // Iterate over all the cache lines and see which ones need to be updated 520 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 521 CacheTextureLine* cl = mCacheLines[i]; 522 if(cl->mDirty) { 523 uint32_t xOffset = 0; 524 uint32_t yOffset = cl->mCurrentRow; 525 uint32_t width = mCacheWidth; 526 uint32_t height = cl->mMaxHeight; 527 void* textureData = mTextTexture + yOffset*width; 528 529 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 530 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 531 532 cl->mDirty = false; 533 } 534 } 535 536 mUploadTexture = false; 537} 538 539void FontRenderer::issueDrawCommand() { 540 checkTextureUpdate(); 541 542 float* vtx = mTextMeshPtr; 543 float* tex = vtx + 3; 544 545 // position is slot 0 546 uint32_t slot = 0; 547 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx); 548 549 // texture0 is slot 1 550 slot = 1; 551 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex); 552 553 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); 554 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 555} 556 557void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, 558 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, 559 float x4, float y4, float z4, float u4, float v4) { 560 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) { 561 return; 562 } 563 564 const uint32_t vertsPerQuad = 4; 565 const uint32_t floatsPerVert = 5; 566 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 567 568 (*currentPos++) = x1; 569 (*currentPos++) = y1; 570 (*currentPos++) = z1; 571 (*currentPos++) = u1; 572 (*currentPos++) = v1; 573 574 (*currentPos++) = x2; 575 (*currentPos++) = y2; 576 (*currentPos++) = z2; 577 (*currentPos++) = u2; 578 (*currentPos++) = v2; 579 580 (*currentPos++) = x3; 581 (*currentPos++) = y3; 582 (*currentPos++) = z3; 583 (*currentPos++) = u3; 584 (*currentPos++) = v3; 585 586 (*currentPos++) = x4; 587 (*currentPos++) = y4; 588 (*currentPos++) = z4; 589 (*currentPos++) = u4; 590 (*currentPos++) = v4; 591 592 mCurrentQuadIndex++; 593 594 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 595 issueDrawCommand(); 596 mCurrentQuadIndex = 0; 597 } 598} 599 600uint32_t FontRenderer::getRemainingCacheCapacity() { 601 uint32_t remainingCapacity = 0; 602 float totalPixels = 0; 603 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 604 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 605 totalPixels += mCacheLines[i]->mMaxWidth; 606 } 607 remainingCapacity = (remainingCapacity * 100) / totalPixels; 608 return remainingCapacity; 609} 610 611void FontRenderer::precacheLatin(SkPaint* paint) { 612 // Remaining capacity is measured in % 613 uint32_t remainingCapacity = getRemainingCacheCapacity(); 614 uint32_t precacheIdx = 0; 615 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 616 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]); 617 remainingCapacity = getRemainingCacheCapacity(); 618 precacheIdx ++; 619 } 620} 621 622void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 623 uint32_t currentNumFonts = mActiveFonts.size(); 624 mCurrentFont = Font::create(this, fontId, fontSize); 625 626 const float maxPrecacheFontSize = 40.0f; 627 bool isNewFont = currentNumFonts != mActiveFonts.size(); 628 629 if (isNewFont && fontSize <= maxPrecacheFontSize) { 630 precacheLatin(paint); 631 } 632} 633FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 634 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { 635 checkInit(); 636 637 if (!mCurrentFont) { 638 DropShadow image; 639 image.width = 0; 640 image.height = 0; 641 image.image = NULL; 642 image.penX = 0; 643 image.penY = 0; 644 return image; 645 } 646 647 Rect bounds; 648 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds); 649 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 650 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 651 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 652 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 653 dataBuffer[i] = 0; 654 } 655 656 int penX = radius - bounds.left; 657 int penY = radius - bounds.bottom; 658 659 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY, 660 dataBuffer, paddedWidth, paddedHeight); 661 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 662 663 DropShadow image; 664 image.width = paddedWidth; 665 image.height = paddedHeight; 666 image.image = dataBuffer; 667 image.penX = penX; 668 image.penY = penY; 669 return image; 670} 671 672void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 673 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) { 674 checkInit(); 675 676 if (!mCurrentFont) { 677 LOGE("No font set"); 678 return; 679 } 680 681 mClip = clip; 682 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); 683 684 if (mCurrentQuadIndex != 0) { 685 issueDrawCommand(); 686 mCurrentQuadIndex = 0; 687 } 688} 689 690void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 691 // Compute gaussian weights for the blur 692 // e is the euler's number 693 float e = 2.718281828459045f; 694 float pi = 3.1415926535897932f; 695 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 696 // x is of the form [-radius .. 0 .. radius] 697 // and sigma varies with radius. 698 // Based on some experimental radius values and sigma's 699 // we approximately fit sigma = f(radius) as 700 // sigma = radius * 0.3 + 0.6 701 // The larger the radius gets, the more our gaussian blur 702 // will resemble a box blur since with large sigma 703 // the gaussian curve begins to lose its shape 704 float sigma = 0.3f * (float)radius + 0.6f; 705 706 // Now compute the coefficints 707 // We will store some redundant values to save some math during 708 // the blur calculations 709 // precompute some values 710 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 711 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 712 713 float normalizeFactor = 0.0f; 714 for(int32_t r = -radius; r <= radius; r ++) { 715 float floatR = (float)r; 716 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 717 normalizeFactor += weights[r + radius]; 718 } 719 720 //Now we need to normalize the weights because all our coefficients need to add up to one 721 normalizeFactor = 1.0f / normalizeFactor; 722 for(int32_t r = -radius; r <= radius; r ++) { 723 weights[r + radius] *= normalizeFactor; 724 } 725} 726 727void FontRenderer::horizontalBlur(float* weights, int32_t radius, 728 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 729 float blurredPixel = 0.0f; 730 float currentPixel = 0.0f; 731 732 for(int32_t y = 0; y < height; y ++) { 733 734 const uint8_t* input = source + y * width; 735 uint8_t* output = dest + y * width; 736 737 for(int32_t x = 0; x < width; x ++) { 738 blurredPixel = 0.0f; 739 const float* gPtr = weights; 740 // Optimization for non-border pixels 741 if ((x > radius) && (x < (width - radius))) { 742 const uint8_t *i = input + (x - radius); 743 for(int r = -radius; r <= radius; r ++) { 744 currentPixel = (float)(*i); 745 blurredPixel += currentPixel * gPtr[0]; 746 gPtr++; 747 i++; 748 } 749 } else { 750 for(int32_t r = -radius; r <= radius; r ++) { 751 // Stepping left and right away from the pixel 752 int validW = x + r; 753 if(validW < 0) { 754 validW = 0; 755 } 756 if(validW > width - 1) { 757 validW = width - 1; 758 } 759 760 currentPixel = (float)(input[validW]); 761 blurredPixel += currentPixel * gPtr[0]; 762 gPtr++; 763 } 764 } 765 *output = (uint8_t)blurredPixel; 766 output ++; 767 } 768 } 769} 770 771void FontRenderer::verticalBlur(float* weights, int32_t radius, 772 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 773 float blurredPixel = 0.0f; 774 float currentPixel = 0.0f; 775 776 for(int32_t y = 0; y < height; y ++) { 777 778 uint8_t* output = dest + y * width; 779 780 for(int32_t x = 0; x < width; x ++) { 781 blurredPixel = 0.0f; 782 const float* gPtr = weights; 783 const uint8_t* input = source + x; 784 // Optimization for non-border pixels 785 if ((y > radius) && (y < (height - radius))) { 786 const uint8_t *i = input + ((y - radius) * width); 787 for(int32_t r = -radius; r <= radius; r ++) { 788 currentPixel = (float)(*i); 789 blurredPixel += currentPixel * gPtr[0]; 790 gPtr++; 791 i += width; 792 } 793 } else { 794 for(int32_t r = -radius; r <= radius; r ++) { 795 int validH = y + r; 796 // Clamp to zero and width 797 if(validH < 0) { 798 validH = 0; 799 } 800 if(validH > height - 1) { 801 validH = height - 1; 802 } 803 804 const uint8_t *i = input + validH * width; 805 currentPixel = (float)(*i); 806 blurredPixel += currentPixel * gPtr[0]; 807 gPtr++; 808 } 809 } 810 *output = (uint8_t)blurredPixel; 811 output ++; 812 } 813 } 814} 815 816 817void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 818 float *gaussian = new float[2 * radius + 1]; 819 computeGaussianWeights(gaussian, radius); 820 uint8_t* scratch = new uint8_t[width * height]; 821 horizontalBlur(gaussian, radius, image, scratch, width, height); 822 verticalBlur(gaussian, radius, scratch, image, width, height); 823 delete[] gaussian; 824 delete[] scratch; 825} 826 827}; // namespace uirenderer 828}; // namespace android 829