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