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