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