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