FontRenderer.cpp revision 61c8c9c5b2006d18e9310b6521c65b36ffe75ce4
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, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 147 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { 148 renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, 149 bitmapW, bitmapH, NULL); 150 } else { 151 renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL); 152 } 153 154} 155 156void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 157 int numGlyphs, Rect *bounds) { 158 if (bounds == NULL) { 159 LOGE("No return rectangle provided to measure text"); 160 return; 161 } 162 bounds->set(1e6, -1e6, -1e6, 1e6); 163 renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); 164} 165 166void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 167 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, 168 uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) { 169 if (numGlyphs == 0 || text == NULL || len == 0) { 170 return; 171 } 172 173 int penX = x, penY = y; 174 int glyphsLeft = 1; 175 if (numGlyphs > 0) { 176 glyphsLeft = numGlyphs; 177 } 178 179 text += start; 180 181 while (glyphsLeft > 0) { 182 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); 183 184 // Reached the end of the string 185 if (utfChar < 0) { 186 break; 187 } 188 189 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); 190 191 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 192 if (cachedGlyph->mIsValid) { 193 switch(mode) { 194 case FRAMEBUFFER: 195 drawCachedGlyph(cachedGlyph, penX, penY); 196 break; 197 case BITMAP: 198 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH); 199 break; 200 case MEASURE: 201 measureCachedGlyph(cachedGlyph, penX, penY, bounds); 202 break; 203 } 204 } 205 206 penX += SkFixedFloor(cachedGlyph->mAdvanceX); 207 208 // If we were given a specific number of glyphs, decrement 209 if (numGlyphs > 0) { 210 glyphsLeft--; 211 } 212 } 213} 214 215void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { 216 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 217 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 218 glyph->mBitmapLeft = skiaGlyph.fLeft; 219 glyph->mBitmapTop = skiaGlyph.fTop; 220 221 uint32_t startX = 0; 222 uint32_t startY = 0; 223 224 // Get the bitmap for the glyph 225 paint->findImage(skiaGlyph); 226 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY); 227 228 if (!glyph->mIsValid) { 229 return; 230 } 231 232 uint32_t endX = startX + skiaGlyph.fWidth; 233 uint32_t endY = startY + skiaGlyph.fHeight; 234 235 glyph->mStartX = startX; 236 glyph->mStartY = startY; 237 glyph->mBitmapWidth = skiaGlyph.fWidth; 238 glyph->mBitmapHeight = skiaGlyph.fHeight; 239 240 uint32_t cacheWidth = mState->getCacheWidth(); 241 uint32_t cacheHeight = mState->getCacheHeight(); 242 243 glyph->mBitmapMinU = (float) startX / (float) cacheWidth; 244 glyph->mBitmapMinV = (float) startY / (float) cacheHeight; 245 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; 246 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; 247 248 mState->mUploadTexture = true; 249} 250 251Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) { 252 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 253 mCachedGlyphs.add(glyph, newGlyph); 254 255 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); 256 newGlyph->mGlyphIndex = skiaGlyph.fID; 257 newGlyph->mIsValid = false; 258 259 updateGlyphCache(paint, skiaGlyph, newGlyph); 260 261 return newGlyph; 262} 263 264Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) { 265 Vector<Font*> &activeFonts = state->mActiveFonts; 266 267 for (uint32_t i = 0; i < activeFonts.size(); i++) { 268 Font* font = activeFonts[i]; 269 if (font->mFontId == fontId && font->mFontSize == fontSize) { 270 return font; 271 } 272 } 273 274 Font* newFont = new Font(state, fontId, fontSize); 275 activeFonts.push(newFont); 276 return newFont; 277} 278 279/////////////////////////////////////////////////////////////////////////////// 280// FontRenderer 281/////////////////////////////////////////////////////////////////////////////// 282 283FontRenderer::FontRenderer() { 284 LOGD("Creating FontRenderer"); 285 286 mInitialized = false; 287 mMaxNumberOfQuads = 1024; 288 mCurrentQuadIndex = 0; 289 mTextureId = 0; 290 291 mTextMeshPtr = NULL; 292 mTextTexture = NULL; 293 294 mIndexBufferID = 0; 295 296 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 297 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 298 299 char property[PROPERTY_VALUE_MAX]; 300 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 301 LOGD(" Setting text cache width to %s pixels", property); 302 mCacheWidth = atoi(property); 303 } else { 304 LOGD(" Using default text cache width of %i pixels", mCacheWidth); 305 } 306 307 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 308 LOGD(" Setting text cache width to %s pixels", property); 309 mCacheHeight = atoi(property); 310 } else { 311 LOGD(" Using default text cache height of %i pixels", mCacheHeight); 312 } 313} 314 315FontRenderer::~FontRenderer() { 316 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 317 delete mCacheLines[i]; 318 } 319 mCacheLines.clear(); 320 321 if (mInitialized) { 322 delete[] mTextMeshPtr; 323 delete[] mTextTexture; 324 } 325 326 if (mTextureId) { 327 glDeleteTextures(1, &mTextureId); 328 } 329 330 Vector<Font*> fontsToDereference = mActiveFonts; 331 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 332 delete fontsToDereference[i]; 333 } 334} 335 336void FontRenderer::flushAllAndInvalidate() { 337 if (mCurrentQuadIndex != 0) { 338 issueDrawCommand(); 339 mCurrentQuadIndex = 0; 340 } 341 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 342 mActiveFonts[i]->invalidateTextureCache(); 343 } 344 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 345 mCacheLines[i]->mCurrentCol = 0; 346 } 347} 348 349bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { 350 // If the glyph is too tall, don't cache it 351 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 352 LOGE("Font size to large to fit in cache. width, height = %i, %i", 353 (int) glyph.fWidth, (int) glyph.fHeight); 354 return false; 355 } 356 357 // Now copy the bitmap into the cache texture 358 uint32_t startX = 0; 359 uint32_t startY = 0; 360 361 bool bitmapFit = false; 362 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 363 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 364 if (bitmapFit) { 365 break; 366 } 367 } 368 369 // If the new glyph didn't fit, flush the state so far and invalidate everything 370 if (!bitmapFit) { 371 flushAllAndInvalidate(); 372 373 // Try to fit it again 374 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 375 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 376 if (bitmapFit) { 377 break; 378 } 379 } 380 381 // if we still don't fit, something is wrong and we shouldn't draw 382 if (!bitmapFit) { 383 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", 384 (int) glyph.fWidth, (int) glyph.fHeight); 385 return false; 386 } 387 } 388 389 *retOriginX = startX; 390 *retOriginY = startY; 391 392 uint32_t endX = startX + glyph.fWidth; 393 uint32_t endY = startY + glyph.fHeight; 394 395 uint32_t cacheWidth = mCacheWidth; 396 397 uint8_t* cacheBuffer = mTextTexture; 398 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 399 unsigned int stride = glyph.rowBytes(); 400 401 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 402 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 403 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 404 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 405 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol; 406 } 407 } 408 409 return true; 410} 411 412void FontRenderer::initTextTexture() { 413 mTextTexture = new uint8_t[mCacheWidth * mCacheHeight]; 414 mUploadTexture = false; 415 416 glGenTextures(1, &mTextureId); 417 glBindTexture(GL_TEXTURE_2D, mTextureId); 418 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 419 // Initialize texture dimentions 420 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, 421 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 422 423 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 424 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 425 426 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 427 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 428 429 // Split up our cache texture into lines of certain widths 430 int nextLine = 0; 431 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0)); 432 nextLine += mCacheLines.top()->mMaxHeight; 433 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 434 nextLine += mCacheLines.top()->mMaxHeight; 435 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 436 nextLine += mCacheLines.top()->mMaxHeight; 437 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 438 nextLine += mCacheLines.top()->mMaxHeight; 439 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 440 nextLine += mCacheLines.top()->mMaxHeight; 441 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0)); 442 nextLine += mCacheLines.top()->mMaxHeight; 443 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); 444} 445 446// Avoid having to reallocate memory and render quad by quad 447void FontRenderer::initVertexArrayBuffers() { 448 uint32_t numIndicies = mMaxNumberOfQuads * 6; 449 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); 450 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 451 452 // Four verts, two triangles , six indices per quad 453 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 454 int i6 = i * 6; 455 int i4 = i * 4; 456 457 indexBufferData[i6 + 0] = i4 + 0; 458 indexBufferData[i6 + 1] = i4 + 1; 459 indexBufferData[i6 + 2] = i4 + 2; 460 461 indexBufferData[i6 + 3] = i4 + 0; 462 indexBufferData[i6 + 4] = i4 + 2; 463 indexBufferData[i6 + 5] = i4 + 3; 464 } 465 466 glGenBuffers(1, &mIndexBufferID); 467 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID); 468 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW); 469 glBindBuffer(GL_ARRAY_BUFFER, 0); 470 471 free(indexBufferData); 472 473 uint32_t coordSize = 3; 474 uint32_t uvSize = 2; 475 uint32_t vertsPerQuad = 4; 476 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 477 mTextMeshPtr = new float[vertexBufferSize]; 478} 479 480// We don't want to allocate anything unless we actually draw text 481void FontRenderer::checkInit() { 482 if (mInitialized) { 483 return; 484 } 485 486 initTextTexture(); 487 initVertexArrayBuffers(); 488 489 // We store a string with letters in a rough frequency of occurrence 490 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); 491 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); 492 mLatinPrecache += String16(",.?!()-+@;:`'"); 493 mLatinPrecache += String16("0123456789"); 494 495 mInitialized = true; 496} 497 498void FontRenderer::checkTextureUpdate() { 499 if (!mUploadTexture) { 500 return; 501 } 502 503 glBindTexture(GL_TEXTURE_2D, mTextureId); 504 505 // Iterate over all the cache lines and see which ones need to be updated 506 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 507 CacheTextureLine* cl = mCacheLines[i]; 508 if(cl->mDirty) { 509 uint32_t xOffset = 0; 510 uint32_t yOffset = cl->mCurrentRow; 511 uint32_t width = mCacheWidth; 512 uint32_t height = cl->mMaxHeight; 513 void* textureData = mTextTexture + yOffset*width; 514 515 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 516 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 517 518 cl->mDirty = false; 519 } 520 } 521 522 mUploadTexture = false; 523} 524 525void FontRenderer::issueDrawCommand() { 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