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