FontRenderer.cpp revision 2d4fd364843d3efc6e6ee59ccc5beb513a86d789
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 mIndexBufferID = 0; 352 353 mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 354 mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 355 356 char property[PROPERTY_VALUE_MAX]; 357 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 358 if (sLogFontRendererCreate) { 359 INIT_LOGD(" Setting text cache width to %s pixels", property); 360 } 361 mSmallCacheWidth = atoi(property); 362 } else { 363 if (sLogFontRendererCreate) { 364 INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth); 365 } 366 } 367 368 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 369 if (sLogFontRendererCreate) { 370 INIT_LOGD(" Setting text cache width to %s pixels", property); 371 } 372 mSmallCacheHeight = atoi(property); 373 } else { 374 if (sLogFontRendererCreate) { 375 INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight); 376 } 377 } 378 379 sLogFontRendererCreate = false; 380} 381 382FontRenderer::~FontRenderer() { 383 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 384 delete mCacheLines[i]; 385 } 386 mCacheLines.clear(); 387 388 if (mInitialized) { 389 delete[] mTextMeshPtr; 390 delete mCacheTextureSmall; 391 delete mCacheTexture128; 392 delete mCacheTexture256; 393 delete mCacheTexture512; 394 } 395 396 Vector<Font*> fontsToDereference = mActiveFonts; 397 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 398 delete fontsToDereference[i]; 399 } 400} 401 402void FontRenderer::flushAllAndInvalidate() { 403 if (mCurrentQuadIndex != 0) { 404 issueDrawCommand(); 405 mCurrentQuadIndex = 0; 406 } 407 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 408 mActiveFonts[i]->invalidateTextureCache(); 409 } 410 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 411 mCacheLines[i]->mCurrentCol = 0; 412 } 413} 414 415uint8_t* FontRenderer::allocateTextureMemory(int width, int height) { 416 uint8_t* textureMemory = new uint8_t[width * height]; 417 memset(textureMemory, 0, width * height * sizeof(uint8_t)); 418 419 return textureMemory; 420} 421 422void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 423 uint32_t* retOriginX, uint32_t* retOriginY) { 424 cachedGlyph->mIsValid = false; 425 // If the glyph is too tall, don't cache it 426 if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 427 LOGE("Font size to large to fit in cache. width, height = %i, %i", 428 (int) glyph.fWidth, (int) glyph.fHeight); 429 return; 430 } 431 432 // Now copy the bitmap into the cache texture 433 uint32_t startX = 0; 434 uint32_t startY = 0; 435 436 bool bitmapFit = false; 437 CacheTextureLine *cacheLine; 438 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 439 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 440 if (bitmapFit) { 441 cacheLine = mCacheLines[i]; 442 break; 443 } 444 } 445 446 // If the new glyph didn't fit, flush the state so far and invalidate everything 447 if (!bitmapFit) { 448 flushAllAndInvalidate(); 449 450 // Try to fit it again 451 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 452 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 453 if (bitmapFit) { 454 cacheLine = mCacheLines[i]; 455 break; 456 } 457 } 458 459 // if we still don't fit, something is wrong and we shouldn't draw 460 if (!bitmapFit) { 461 return; 462 } 463 } 464 465 cachedGlyph->mCachedTextureLine = cacheLine; 466 467 *retOriginX = startX; 468 *retOriginY = startY; 469 470 uint32_t endX = startX + glyph.fWidth; 471 uint32_t endY = startY + glyph.fHeight; 472 473 uint32_t cacheWidth = cacheLine->mMaxWidth; 474 475 CacheTexture *cacheTexture = cacheLine->mCacheTexture; 476 if (cacheTexture->mTexture == NULL) { 477 // Large-glyph texture memory is allocated only as needed 478 cacheTexture->mTexture = allocateTextureMemory(cacheTexture->mWidth, cacheTexture->mHeight); 479 } 480 uint8_t* cacheBuffer = cacheTexture->mTexture; 481 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 482 unsigned int stride = glyph.rowBytes(); 483 484 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 485 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 486 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 487 uint8_t tempCol = bitmapBuffer[bY * stride + bX]; 488 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 489 } 490 } 491 cachedGlyph->mIsValid = true; 492} 493 494CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 495 uint8_t* textureMemory = allocate ? allocateTextureMemory(width, height) : NULL; 496 GLuint textureId; 497 glGenTextures(1, &textureId); 498 glBindTexture(GL_TEXTURE_2D, textureId); 499 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 500 // Initialize texture dimensions 501 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, 502 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 503 504 mLinearFiltering = false; 505 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 506 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 507 508 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 509 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 510 511 return new CacheTexture(textureMemory, textureId, width, height); 512} 513 514void FontRenderer::initTextTexture() { 515 mCacheLines.clear(); 516 517 // Next, use other, separate caches for large glyphs. 518 uint16_t maxWidth = 0; 519 if (Caches::hasInstance()) { 520 maxWidth = Caches::getInstance().maxTextureSize; 521 } 522 if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) { 523 maxWidth = MAX_TEXT_CACHE_WIDTH; 524 } 525 if (mCacheTextureSmall != NULL) { 526 delete mCacheTextureSmall; 527 delete mCacheTexture128; 528 delete mCacheTexture256; 529 delete mCacheTexture512; 530 } 531 mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true); 532 mCacheTexture128 = createCacheTexture(maxWidth, 256, false); 533 mCacheTexture256 = createCacheTexture(maxWidth, 256, false); 534 mCacheTexture512 = createCacheTexture(maxWidth, 512, false); 535 mCurrentCacheTexture = mCacheTextureSmall; 536 537 mUploadTexture = false; 538 // Split up our default cache texture into lines of certain widths 539 int nextLine = 0; 540 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall)); 541 nextLine += mCacheLines.top()->mMaxHeight; 542 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 543 nextLine += mCacheLines.top()->mMaxHeight; 544 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); 545 nextLine += mCacheLines.top()->mMaxHeight; 546 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 547 nextLine += mCacheLines.top()->mMaxHeight; 548 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); 549 nextLine += mCacheLines.top()->mMaxHeight; 550 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall)); 551 nextLine += mCacheLines.top()->mMaxHeight; 552 mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine, 553 nextLine, 0, mCacheTextureSmall)); 554 555 // The first cache is split into 2 lines of height 128, the rest have just one cache line. 556 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128)); 557 mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128)); 558 mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256)); 559 mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512)); 560} 561 562// Avoid having to reallocate memory and render quad by quad 563void FontRenderer::initVertexArrayBuffers() { 564 uint32_t numIndices = mMaxNumberOfQuads * 6; 565 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 566 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 567 568 // Four verts, two triangles , six indices per quad 569 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 570 int i6 = i * 6; 571 int i4 = i * 4; 572 573 indexBufferData[i6 + 0] = i4 + 0; 574 indexBufferData[i6 + 1] = i4 + 1; 575 indexBufferData[i6 + 2] = i4 + 2; 576 577 indexBufferData[i6 + 3] = i4 + 0; 578 indexBufferData[i6 + 4] = i4 + 2; 579 indexBufferData[i6 + 5] = i4 + 3; 580 } 581 582 glGenBuffers(1, &mIndexBufferID); 583 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 584 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 585 586 free(indexBufferData); 587 588 uint32_t coordSize = 2; 589 uint32_t uvSize = 2; 590 uint32_t vertsPerQuad = 4; 591 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 592 mTextMeshPtr = new float[vertexBufferSize]; 593} 594 595// We don't want to allocate anything unless we actually draw text 596void FontRenderer::checkInit() { 597 if (mInitialized) { 598 return; 599 } 600 601 initTextTexture(); 602 initVertexArrayBuffers(); 603 604 // We store a string with letters in a rough frequency of occurrence 605 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); 606 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); 607 mLatinPrecache += String16(",.?!()-+@;:`'"); 608 mLatinPrecache += String16("0123456789"); 609 610 mInitialized = true; 611} 612 613void FontRenderer::checkTextureUpdate() { 614 if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) { 615 return; 616 } 617 618 Caches& caches = Caches::getInstance(); 619 GLuint lastTextureId = 0; 620 // Iterate over all the cache lines and see which ones need to be updated 621 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 622 CacheTextureLine* cl = mCacheLines[i]; 623 if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) { 624 CacheTexture* cacheTexture = cl->mCacheTexture; 625 uint32_t xOffset = 0; 626 uint32_t yOffset = cl->mCurrentRow; 627 uint32_t width = cl->mMaxWidth; 628 uint32_t height = cl->mMaxHeight; 629 void* textureData = cacheTexture->mTexture + (yOffset * width); 630 631 if (cacheTexture->mTextureId != lastTextureId) { 632 caches.activeTexture(0); 633 glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); 634 lastTextureId = cacheTexture->mTextureId; 635 } 636 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 637 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 638 639 cl->mDirty = false; 640 } 641 } 642 643 glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); 644 mLastCacheTexture = mCurrentCacheTexture; 645 646 mUploadTexture = false; 647} 648 649void FontRenderer::issueDrawCommand() { 650 checkTextureUpdate(); 651 652 Caches& caches = Caches::getInstance(); 653 caches.bindIndicesBuffer(mIndexBufferID); 654 if (!mDrawn) { 655 float* buffer = mTextMeshPtr; 656 int offset = 2; 657 658 bool force = caches.unbindMeshBuffer(); 659 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 660 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 661 buffer + offset); 662 } 663 664 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 665 666 mDrawn = true; 667} 668 669void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 670 float x2, float y2, float u2, float v2, 671 float x3, float y3, float u3, float v3, 672 float x4, float y4, float u4, float v4, CacheTexture* texture) { 673 674 if (mClip && 675 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 676 return; 677 } 678 if (texture != mCurrentCacheTexture) { 679 if (mCurrentQuadIndex != 0) { 680 // First, draw everything stored already which uses the previous texture 681 issueDrawCommand(); 682 mCurrentQuadIndex = 0; 683 } 684 // Now use the new texture id 685 mCurrentCacheTexture = texture; 686 } 687 688 const uint32_t vertsPerQuad = 4; 689 const uint32_t floatsPerVert = 4; 690 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 691 692 (*currentPos++) = x1; 693 (*currentPos++) = y1; 694 (*currentPos++) = u1; 695 (*currentPos++) = v1; 696 697 (*currentPos++) = x2; 698 (*currentPos++) = y2; 699 (*currentPos++) = u2; 700 (*currentPos++) = v2; 701 702 (*currentPos++) = x3; 703 (*currentPos++) = y3; 704 (*currentPos++) = u3; 705 (*currentPos++) = v3; 706 707 (*currentPos++) = x4; 708 (*currentPos++) = y4; 709 (*currentPos++) = u4; 710 (*currentPos++) = v4; 711 712 mCurrentQuadIndex++; 713 714 if (mBounds) { 715 mBounds->left = fmin(mBounds->left, x1); 716 mBounds->top = fmin(mBounds->top, y3); 717 mBounds->right = fmax(mBounds->right, x3); 718 mBounds->bottom = fmax(mBounds->bottom, y1); 719 } 720 721 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 722 issueDrawCommand(); 723 mCurrentQuadIndex = 0; 724 } 725} 726 727uint32_t FontRenderer::getRemainingCacheCapacity() { 728 uint32_t remainingCapacity = 0; 729 float totalPixels = 0; 730 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 731 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 732 totalPixels += mCacheLines[i]->mMaxWidth; 733 } 734 remainingCapacity = (remainingCapacity * 100) / totalPixels; 735 return remainingCapacity; 736} 737 738void FontRenderer::precacheLatin(SkPaint* paint) { 739 // Remaining capacity is measured in % 740 uint32_t remainingCapacity = getRemainingCacheCapacity(); 741 uint32_t precacheIdx = 0; 742 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 743 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]); 744 remainingCapacity = getRemainingCacheCapacity(); 745 precacheIdx ++; 746 } 747} 748 749void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 750 uint32_t currentNumFonts = mActiveFonts.size(); 751 int flags = 0; 752 if (paint->isFakeBoldText()) { 753 flags |= Font::kFakeBold; 754 } 755 756 const float skewX = paint->getTextSkewX(); 757 uint32_t italicStyle = *(uint32_t*) &skewX; 758 const float scaleXFloat = paint->getTextScaleX(); 759 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 760 SkPaint::Style style = paint->getStyle(); 761 const float strokeWidthFloat = paint->getStrokeWidth(); 762 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 763 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 764 scaleX, style, strokeWidth); 765 766 const float maxPrecacheFontSize = 40.0f; 767 bool isNewFont = currentNumFonts != mActiveFonts.size(); 768 769 if (isNewFont && fontSize <= maxPrecacheFontSize) { 770 precacheLatin(paint); 771 } 772} 773 774FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 775 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { 776 checkInit(); 777 778 if (!mCurrentFont) { 779 DropShadow image; 780 image.width = 0; 781 image.height = 0; 782 image.image = NULL; 783 image.penX = 0; 784 image.penY = 0; 785 return image; 786 } 787 788 mDrawn = false; 789 mClip = NULL; 790 mBounds = NULL; 791 792 Rect bounds; 793 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds); 794 795 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 796 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 797 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 798 799 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 800 dataBuffer[i] = 0; 801 } 802 803 int penX = radius - bounds.left; 804 int penY = radius - bounds.bottom; 805 806 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 807 dataBuffer, paddedWidth, paddedHeight); 808 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 809 810 DropShadow image; 811 image.width = paddedWidth; 812 image.height = paddedHeight; 813 image.image = dataBuffer; 814 image.penX = penX; 815 image.penY = penY; 816 817 return image; 818} 819 820bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 821 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 822 checkInit(); 823 824 if (!mCurrentFont) { 825 LOGE("No font set"); 826 return false; 827 } 828 829 mDrawn = false; 830 mBounds = bounds; 831 mClip = clip; 832 833 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 834 835 mBounds = NULL; 836 mClip = NULL; 837 838 if (mCurrentQuadIndex != 0) { 839 issueDrawCommand(); 840 mCurrentQuadIndex = 0; 841 } 842 843 return mDrawn; 844} 845 846void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 847 // Compute gaussian weights for the blur 848 // e is the euler's number 849 float e = 2.718281828459045f; 850 float pi = 3.1415926535897932f; 851 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 852 // x is of the form [-radius .. 0 .. radius] 853 // and sigma varies with radius. 854 // Based on some experimental radius values and sigma's 855 // we approximately fit sigma = f(radius) as 856 // sigma = radius * 0.3 + 0.6 857 // The larger the radius gets, the more our gaussian blur 858 // will resemble a box blur since with large sigma 859 // the gaussian curve begins to lose its shape 860 float sigma = 0.3f * (float) radius + 0.6f; 861 862 // Now compute the coefficints 863 // We will store some redundant values to save some math during 864 // the blur calculations 865 // precompute some values 866 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 867 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 868 869 float normalizeFactor = 0.0f; 870 for (int32_t r = -radius; r <= radius; r ++) { 871 float floatR = (float) r; 872 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 873 normalizeFactor += weights[r + radius]; 874 } 875 876 //Now we need to normalize the weights because all our coefficients need to add up to one 877 normalizeFactor = 1.0f / normalizeFactor; 878 for (int32_t r = -radius; r <= radius; r ++) { 879 weights[r + radius] *= normalizeFactor; 880 } 881} 882 883void FontRenderer::horizontalBlur(float* weights, int32_t radius, 884 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 885 float blurredPixel = 0.0f; 886 float currentPixel = 0.0f; 887 888 for (int32_t y = 0; y < height; y ++) { 889 890 const uint8_t* input = source + y * width; 891 uint8_t* output = dest + y * width; 892 893 for (int32_t x = 0; x < width; x ++) { 894 blurredPixel = 0.0f; 895 const float* gPtr = weights; 896 // Optimization for non-border pixels 897 if (x > radius && x < (width - radius)) { 898 const uint8_t *i = input + (x - radius); 899 for (int r = -radius; r <= radius; r ++) { 900 currentPixel = (float) (*i); 901 blurredPixel += currentPixel * gPtr[0]; 902 gPtr++; 903 i++; 904 } 905 } else { 906 for (int32_t r = -radius; r <= radius; r ++) { 907 // Stepping left and right away from the pixel 908 int validW = x + r; 909 if (validW < 0) { 910 validW = 0; 911 } 912 if (validW > width - 1) { 913 validW = width - 1; 914 } 915 916 currentPixel = (float) input[validW]; 917 blurredPixel += currentPixel * gPtr[0]; 918 gPtr++; 919 } 920 } 921 *output = (uint8_t)blurredPixel; 922 output ++; 923 } 924 } 925} 926 927void FontRenderer::verticalBlur(float* weights, int32_t radius, 928 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 929 float blurredPixel = 0.0f; 930 float currentPixel = 0.0f; 931 932 for (int32_t y = 0; y < height; y ++) { 933 934 uint8_t* output = dest + y * width; 935 936 for (int32_t x = 0; x < width; x ++) { 937 blurredPixel = 0.0f; 938 const float* gPtr = weights; 939 const uint8_t* input = source + x; 940 // Optimization for non-border pixels 941 if (y > radius && y < (height - radius)) { 942 const uint8_t *i = input + ((y - radius) * width); 943 for (int32_t r = -radius; r <= radius; r ++) { 944 currentPixel = (float)(*i); 945 blurredPixel += currentPixel * gPtr[0]; 946 gPtr++; 947 i += width; 948 } 949 } else { 950 for (int32_t r = -radius; r <= radius; r ++) { 951 int validH = y + r; 952 // Clamp to zero and width 953 if (validH < 0) { 954 validH = 0; 955 } 956 if (validH > height - 1) { 957 validH = height - 1; 958 } 959 960 const uint8_t *i = input + validH * width; 961 currentPixel = (float) (*i); 962 blurredPixel += currentPixel * gPtr[0]; 963 gPtr++; 964 } 965 } 966 *output = (uint8_t) blurredPixel; 967 output ++; 968 } 969 } 970} 971 972 973void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 974 float *gaussian = new float[2 * radius + 1]; 975 computeGaussianWeights(gaussian, radius); 976 977 uint8_t* scratch = new uint8_t[width * height]; 978 979 horizontalBlur(gaussian, radius, image, scratch, width, height); 980 verticalBlur(gaussian, radius, scratch, image, width, height); 981 982 delete[] gaussian; 983 delete[] scratch; 984} 985 986}; // namespace uirenderer 987}; // namespace android 988