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