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