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