FontRenderer.cpp revision 9cccc2b9bdd4850a3f9679569aaec3ab98477a5d
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 = mCachedGlyphs.valueFor(utfChar); 132 if (cachedGlyph == NULL) { 133 cachedGlyph = cacheGlyph(paint, utfChar); 134 } 135 136 // Is the glyph still in texture cache? 137 if (!cachedGlyph->mIsValid) { 138 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar); 139 updateGlyphCache(paint, skiaGlyph, cachedGlyph); 140 } 141 142 return cachedGlyph; 143} 144 145void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 146 int numGlyphs, int x, int y, 147 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 148 if(bitmap != NULL && bitmapW > 0 && bitmapH > 0) { 149 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, 150 bitmap, bitmapW, bitmapH, NULL); 151 } 152 else { 153 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, 154 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, 171 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, 172 Rect *bounds) { 173 if (numGlyphs == 0 || text == NULL || len == 0) { 174 return; 175 } 176 177 int penX = x, penY = y; 178 int glyphsLeft = 1; 179 if (numGlyphs > 0) { 180 glyphsLeft = numGlyphs; 181 } 182 183 text += start; 184 185 while (glyphsLeft > 0) { 186 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); 187 188 // Reached the end of the string or encountered 189 if (utfChar < 0) { 190 break; 191 } 192 193 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); 194 195 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 196 if (cachedGlyph->mIsValid) { 197 switch(mode) { 198 case FRAMEBUFFER: 199 drawCachedGlyph(cachedGlyph, penX, penY); 200 break; 201 case BITMAP: 202 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH); 203 break; 204 case MEASURE: 205 measureCachedGlyph(cachedGlyph, penX, penY, bounds); 206 break; 207 } 208 } 209 210 penX += SkFixedFloor(cachedGlyph->mAdvanceX); 211 212 // If we were given a specific number of glyphs, decrement 213 if (numGlyphs > 0) { 214 glyphsLeft--; 215 } 216 } 217} 218 219void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { 220 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 221 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 222 glyph->mBitmapLeft = skiaGlyph.fLeft; 223 glyph->mBitmapTop = skiaGlyph.fTop; 224 225 uint32_t startX = 0; 226 uint32_t startY = 0; 227 228 // Get the bitmap for the glyph 229 paint->findImage(skiaGlyph); 230 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY); 231 232 if (!glyph->mIsValid) { 233 return; 234 } 235 236 uint32_t endX = startX + skiaGlyph.fWidth; 237 uint32_t endY = startY + skiaGlyph.fHeight; 238 239 glyph->mStartX = startX; 240 glyph->mStartY = startY; 241 glyph->mBitmapWidth = skiaGlyph.fWidth; 242 glyph->mBitmapHeight = skiaGlyph.fHeight; 243 244 uint32_t cacheWidth = mState->getCacheWidth(); 245 uint32_t cacheHeight = mState->getCacheHeight(); 246 247 glyph->mBitmapMinU = (float) startX / (float) cacheWidth; 248 glyph->mBitmapMinV = (float) startY / (float) cacheHeight; 249 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; 250 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; 251 252 mState->mUploadTexture = true; 253} 254 255Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) { 256 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 257 mCachedGlyphs.add(glyph, newGlyph); 258 259 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); 260 newGlyph->mGlyphIndex = skiaGlyph.fID; 261 newGlyph->mIsValid = false; 262 263 updateGlyphCache(paint, skiaGlyph, newGlyph); 264 265 return newGlyph; 266} 267 268Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) { 269 Vector<Font*> &activeFonts = state->mActiveFonts; 270 271 for (uint32_t i = 0; i < activeFonts.size(); i++) { 272 Font* font = activeFonts[i]; 273 if (font->mFontId == fontId && font->mFontSize == fontSize) { 274 return font; 275 } 276 } 277 278 Font* newFont = new Font(state, fontId, fontSize); 279 activeFonts.push(newFont); 280 return newFont; 281} 282 283/////////////////////////////////////////////////////////////////////////////// 284// FontRenderer 285/////////////////////////////////////////////////////////////////////////////// 286 287FontRenderer::FontRenderer() { 288 LOGD("Creating FontRenderer"); 289 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] = 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 531 checkTextureUpdate(); 532 533 float* vtx = mTextMeshPtr; 534 float* tex = vtx + 3; 535 536 // position is slot 0 537 uint32_t slot = 0; 538 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx); 539 540 // texture0 is slot 1 541 slot = 1; 542 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex); 543 544 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); 545 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 546} 547 548void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, 549 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, 550 float x4, float y4, float z4, float u4, float v4) { 551 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) { 552 return; 553 } 554 555 const uint32_t vertsPerQuad = 4; 556 const uint32_t floatsPerVert = 5; 557 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 558 559 (*currentPos++) = x1; 560 (*currentPos++) = y1; 561 (*currentPos++) = z1; 562 (*currentPos++) = u1; 563 (*currentPos++) = v1; 564 565 (*currentPos++) = x2; 566 (*currentPos++) = y2; 567 (*currentPos++) = z2; 568 (*currentPos++) = u2; 569 (*currentPos++) = v2; 570 571 (*currentPos++) = x3; 572 (*currentPos++) = y3; 573 (*currentPos++) = z3; 574 (*currentPos++) = u3; 575 (*currentPos++) = v3; 576 577 (*currentPos++) = x4; 578 (*currentPos++) = y4; 579 (*currentPos++) = z4; 580 (*currentPos++) = u4; 581 (*currentPos++) = v4; 582 583 mCurrentQuadIndex++; 584 585 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 586 issueDrawCommand(); 587 mCurrentQuadIndex = 0; 588 } 589} 590 591uint32_t FontRenderer::getRemainingCacheCapacity() { 592 uint32_t remainingCapacity = 0; 593 float totalPixels = 0; 594 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 595 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 596 totalPixels += mCacheLines[i]->mMaxWidth; 597 } 598 remainingCapacity = (remainingCapacity * 100) / totalPixels; 599 return remainingCapacity; 600} 601 602void FontRenderer::precacheLatin(SkPaint* paint) { 603 // Remaining capacity is measured in % 604 uint32_t remainingCapacity = getRemainingCacheCapacity(); 605 uint32_t precacheIdx = 0; 606 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 607 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]); 608 remainingCapacity = getRemainingCacheCapacity(); 609 precacheIdx ++; 610 } 611} 612 613void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 614 uint32_t currentNumFonts = mActiveFonts.size(); 615 mCurrentFont = Font::create(this, fontId, fontSize); 616 617 const float maxPrecacheFontSize = 40.0f; 618 bool isNewFont = currentNumFonts != mActiveFonts.size(); 619 620 if(isNewFont && fontSize <= maxPrecacheFontSize ){ 621 precacheLatin(paint); 622 } 623} 624FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 625 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { 626 627 Rect bounds; 628 mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds); 629 uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2*radius; 630 uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2*radius; 631 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 632 for(uint32_t i = 0; i < paddedWidth * paddedHeight; i ++) { 633 dataBuffer[i] = 0; 634 } 635 int penX = radius - bounds.left; 636 int penY = radius - bounds.bottom; 637 638 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY, 639 dataBuffer, paddedWidth, paddedHeight); 640 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 641 642 DropShadow image; 643 image.width = paddedWidth; 644 image.height = paddedHeight; 645 image.image = dataBuffer; 646 image.penX = penX; 647 image.penY = penY; 648 return image; 649} 650 651void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 652 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) { 653 checkInit(); 654 655 if (!mCurrentFont) { 656 LOGE("No font set"); 657 return; 658 } 659 660 mClip = clip; 661 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); 662 663 if (mCurrentQuadIndex != 0) { 664 issueDrawCommand(); 665 mCurrentQuadIndex = 0; 666 } 667} 668 669void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 670 // Compute gaussian weights for the blur 671 // e is the euler's number 672 float e = 2.718281828459045f; 673 float pi = 3.1415926535897932f; 674 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 675 // x is of the form [-radius .. 0 .. radius] 676 // and sigma varies with radius. 677 // Based on some experimental radius values and sigma's 678 // we approximately fit sigma = f(radius) as 679 // sigma = radius * 0.3 + 0.6 680 // The larger the radius gets, the more our gaussian blur 681 // will resemble a box blur since with large sigma 682 // the gaussian curve begins to lose its shape 683 float sigma = 0.3f * (float)radius + 0.6f; 684 685 // Now compute the coefficints 686 // We will store some redundant values to save some math during 687 // the blur calculations 688 // precompute some values 689 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 690 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 691 692 float normalizeFactor = 0.0f; 693 for(int32_t r = -radius; r <= radius; r ++) { 694 float floatR = (float)r; 695 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 696 normalizeFactor += weights[r + radius]; 697 } 698 699 //Now we need to normalize the weights because all our coefficients need to add up to one 700 normalizeFactor = 1.0f / normalizeFactor; 701 for(int32_t r = -radius; r <= radius; r ++) { 702 weights[r + radius] *= normalizeFactor; 703 } 704} 705 706void FontRenderer::horizontalBlur(float* weights, int32_t radius, 707 const uint8_t* source, uint8_t* dest, 708 int32_t width, int32_t height) { 709 float blurredPixel = 0.0f; 710 float currentPixel = 0.0f; 711 712 for(int32_t y = 0; y < height; y ++) { 713 714 const uint8_t* input = source + y * width; 715 uint8_t* output = dest + y * width; 716 717 for(int32_t x = 0; x < width; x ++) { 718 blurredPixel = 0.0f; 719 const float* gPtr = weights; 720 // Optimization for non-border pixels 721 if ((x > radius) && (x < (width - radius))) { 722 const uint8_t *i = input + (x - radius); 723 for(int r = -radius; r <= radius; r ++) { 724 currentPixel = (float)(*i); 725 blurredPixel += currentPixel * gPtr[0]; 726 gPtr++; 727 i++; 728 } 729 } else { 730 for(int32_t r = -radius; r <= radius; r ++) { 731 // Stepping left and right away from the pixel 732 int validW = x + r; 733 if(validW < 0) { 734 validW = 0; 735 } 736 if(validW > width - 1) { 737 validW = width - 1; 738 } 739 740 currentPixel = (float)(input[validW]); 741 blurredPixel += currentPixel * gPtr[0]; 742 gPtr++; 743 } 744 } 745 *output = (uint8_t)blurredPixel; 746 output ++; 747 } 748 } 749} 750 751void FontRenderer::verticalBlur(float* weights, int32_t radius, 752 const uint8_t* source, uint8_t* dest, 753 int32_t width, int32_t height) { 754 float blurredPixel = 0.0f; 755 float currentPixel = 0.0f; 756 757 for(int32_t y = 0; y < height; y ++) { 758 759 uint8_t* output = dest + y * width; 760 761 for(int32_t x = 0; x < width; x ++) { 762 blurredPixel = 0.0f; 763 const float* gPtr = weights; 764 const uint8_t* input = source + x; 765 // Optimization for non-border pixels 766 if ((y > radius) && (y < (height - radius))) { 767 const uint8_t *i = input + ((y - radius) * width); 768 for(int32_t r = -radius; r <= radius; r ++) { 769 currentPixel = (float)(*i); 770 blurredPixel += currentPixel * gPtr[0]; 771 gPtr++; 772 i += width; 773 } 774 } else { 775 for(int32_t r = -radius; r <= radius; r ++) { 776 int validH = y + r; 777 // Clamp to zero and width 778 if(validH < 0) { 779 validH = 0; 780 } 781 if(validH > height - 1) { 782 validH = height - 1; 783 } 784 785 const uint8_t *i = input + validH * width; 786 currentPixel = (float)(*i); 787 blurredPixel += currentPixel * gPtr[0]; 788 gPtr++; 789 } 790 } 791 *output = (uint8_t)blurredPixel; 792 output ++; 793 } 794 } 795} 796 797 798void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 799 float *gaussian = new float[2 * radius + 1]; 800 computeGaussianWeights(gaussian, radius); 801 uint8_t* scratch = new uint8_t[width * height]; 802 horizontalBlur(gaussian, radius, image, scratch, width, height); 803 verticalBlur(gaussian, radius, scratch, image, width, height); 804 delete[] gaussian; 805 delete[] scratch; 806} 807 808}; // namespace uirenderer 809}; // namespace android 810