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