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