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