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