FontRenderer.cpp revision e829bc0f0364e942bed01536d115a5c08d25d776
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 // 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 Caches& caches = Caches::getInstance(); 647 if (!mDrawn) { 648 float* buffer = mTextMeshPtr; 649 int offset = 2; 650 651 bool force = caches.unbindMeshBuffer(); 652 caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); 653 caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, 654 buffer + offset); 655 } 656 657 caches.bindIndicesBuffer(mIndexBufferID); 658 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 659 660 mDrawn = true; 661} 662 663void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 664 float x2, float y2, float u2, float v2, 665 float x3, float y3, float u3, float v3, 666 float x4, float y4, float u4, float v4, CacheTexture* texture) { 667 668 if (mClip && 669 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 670 return; 671 } 672 if (texture != mCurrentCacheTexture) { 673 if (mCurrentQuadIndex != 0) { 674 // First, draw everything stored already which uses the previous texture 675 issueDrawCommand(); 676 mCurrentQuadIndex = 0; 677 } 678 // Now use the new texture id 679 mCurrentCacheTexture = texture; 680 } 681 682 const uint32_t vertsPerQuad = 4; 683 const uint32_t floatsPerVert = 4; 684 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 685 686 (*currentPos++) = x1; 687 (*currentPos++) = y1; 688 (*currentPos++) = u1; 689 (*currentPos++) = v1; 690 691 (*currentPos++) = x2; 692 (*currentPos++) = y2; 693 (*currentPos++) = u2; 694 (*currentPos++) = v2; 695 696 (*currentPos++) = x3; 697 (*currentPos++) = y3; 698 (*currentPos++) = u3; 699 (*currentPos++) = v3; 700 701 (*currentPos++) = x4; 702 (*currentPos++) = y4; 703 (*currentPos++) = u4; 704 (*currentPos++) = v4; 705 706 mCurrentQuadIndex++; 707 708 if (mBounds) { 709 mBounds->left = fmin(mBounds->left, x1); 710 mBounds->top = fmin(mBounds->top, y3); 711 mBounds->right = fmax(mBounds->right, x3); 712 mBounds->bottom = fmax(mBounds->bottom, y1); 713 } 714 715 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 716 issueDrawCommand(); 717 mCurrentQuadIndex = 0; 718 } 719} 720 721uint32_t FontRenderer::getRemainingCacheCapacity() { 722 uint32_t remainingCapacity = 0; 723 float totalPixels = 0; 724 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 725 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 726 totalPixels += mCacheLines[i]->mMaxWidth; 727 } 728 remainingCapacity = (remainingCapacity * 100) / totalPixels; 729 return remainingCapacity; 730} 731 732void FontRenderer::precacheLatin(SkPaint* paint) { 733 // Remaining capacity is measured in % 734 uint32_t remainingCapacity = getRemainingCacheCapacity(); 735 uint32_t precacheIdx = 0; 736 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 737 mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]); 738 remainingCapacity = getRemainingCacheCapacity(); 739 precacheIdx ++; 740 } 741} 742 743void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 744 uint32_t currentNumFonts = mActiveFonts.size(); 745 int flags = 0; 746 if (paint->isFakeBoldText()) { 747 flags |= Font::kFakeBold; 748 } 749 750 const float skewX = paint->getTextSkewX(); 751 uint32_t italicStyle = *(uint32_t*) &skewX; 752 const float scaleXFloat = paint->getTextScaleX(); 753 uint32_t scaleX = *(uint32_t*) &scaleXFloat; 754 SkPaint::Style style = paint->getStyle(); 755 const float strokeWidthFloat = paint->getStrokeWidth(); 756 uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat; 757 mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, 758 scaleX, style, strokeWidth); 759 760 const float maxPrecacheFontSize = 40.0f; 761 bool isNewFont = currentNumFonts != mActiveFonts.size(); 762 763 if (isNewFont && fontSize <= maxPrecacheFontSize) { 764 precacheLatin(paint); 765 } 766} 767 768FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 769 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { 770 checkInit(); 771 772 if (!mCurrentFont) { 773 DropShadow image; 774 image.width = 0; 775 image.height = 0; 776 image.image = NULL; 777 image.penX = 0; 778 image.penY = 0; 779 return image; 780 } 781 782 mClip = NULL; 783 mBounds = NULL; 784 785 Rect bounds; 786 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds); 787 788 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 789 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 790 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 791 792 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 793 dataBuffer[i] = 0; 794 } 795 796 int penX = radius - bounds.left; 797 int penY = radius - bounds.bottom; 798 799 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 800 dataBuffer, paddedWidth, paddedHeight); 801 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 802 803 DropShadow image; 804 image.width = paddedWidth; 805 image.height = paddedHeight; 806 image.image = dataBuffer; 807 image.penX = penX; 808 image.penY = penY; 809 return image; 810} 811 812bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 813 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 814 checkInit(); 815 816 if (!mCurrentFont) { 817 LOGE("No font set"); 818 return false; 819 } 820 821 mDrawn = false; 822 mBounds = bounds; 823 mClip = clip; 824 825 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 826 827 mBounds = NULL; 828 mClip = NULL; 829 830 if (mCurrentQuadIndex != 0) { 831 issueDrawCommand(); 832 mCurrentQuadIndex = 0; 833 } 834 835 return mDrawn; 836} 837 838void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 839 // Compute gaussian weights for the blur 840 // e is the euler's number 841 float e = 2.718281828459045f; 842 float pi = 3.1415926535897932f; 843 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 844 // x is of the form [-radius .. 0 .. radius] 845 // and sigma varies with radius. 846 // Based on some experimental radius values and sigma's 847 // we approximately fit sigma = f(radius) as 848 // sigma = radius * 0.3 + 0.6 849 // The larger the radius gets, the more our gaussian blur 850 // will resemble a box blur since with large sigma 851 // the gaussian curve begins to lose its shape 852 float sigma = 0.3f * (float) radius + 0.6f; 853 854 // Now compute the coefficints 855 // We will store some redundant values to save some math during 856 // the blur calculations 857 // precompute some values 858 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 859 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 860 861 float normalizeFactor = 0.0f; 862 for (int32_t r = -radius; r <= radius; r ++) { 863 float floatR = (float) r; 864 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 865 normalizeFactor += weights[r + radius]; 866 } 867 868 //Now we need to normalize the weights because all our coefficients need to add up to one 869 normalizeFactor = 1.0f / normalizeFactor; 870 for (int32_t r = -radius; r <= radius; r ++) { 871 weights[r + radius] *= normalizeFactor; 872 } 873} 874 875void FontRenderer::horizontalBlur(float* weights, int32_t radius, 876 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 877 float blurredPixel = 0.0f; 878 float currentPixel = 0.0f; 879 880 for (int32_t y = 0; y < height; y ++) { 881 882 const uint8_t* input = source + y * width; 883 uint8_t* output = dest + y * width; 884 885 for (int32_t x = 0; x < width; x ++) { 886 blurredPixel = 0.0f; 887 const float* gPtr = weights; 888 // Optimization for non-border pixels 889 if (x > radius && x < (width - radius)) { 890 const uint8_t *i = input + (x - radius); 891 for (int r = -radius; r <= radius; r ++) { 892 currentPixel = (float) (*i); 893 blurredPixel += currentPixel * gPtr[0]; 894 gPtr++; 895 i++; 896 } 897 } else { 898 for (int32_t r = -radius; r <= radius; r ++) { 899 // Stepping left and right away from the pixel 900 int validW = x + r; 901 if (validW < 0) { 902 validW = 0; 903 } 904 if (validW > width - 1) { 905 validW = width - 1; 906 } 907 908 currentPixel = (float) input[validW]; 909 blurredPixel += currentPixel * gPtr[0]; 910 gPtr++; 911 } 912 } 913 *output = (uint8_t)blurredPixel; 914 output ++; 915 } 916 } 917} 918 919void FontRenderer::verticalBlur(float* weights, int32_t radius, 920 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 921 float blurredPixel = 0.0f; 922 float currentPixel = 0.0f; 923 924 for (int32_t y = 0; y < height; y ++) { 925 926 uint8_t* output = dest + y * width; 927 928 for (int32_t x = 0; x < width; x ++) { 929 blurredPixel = 0.0f; 930 const float* gPtr = weights; 931 const uint8_t* input = source + x; 932 // Optimization for non-border pixels 933 if (y > radius && y < (height - radius)) { 934 const uint8_t *i = input + ((y - radius) * width); 935 for (int32_t r = -radius; r <= radius; r ++) { 936 currentPixel = (float)(*i); 937 blurredPixel += currentPixel * gPtr[0]; 938 gPtr++; 939 i += width; 940 } 941 } else { 942 for (int32_t r = -radius; r <= radius; r ++) { 943 int validH = y + r; 944 // Clamp to zero and width 945 if (validH < 0) { 946 validH = 0; 947 } 948 if (validH > height - 1) { 949 validH = height - 1; 950 } 951 952 const uint8_t *i = input + validH * width; 953 currentPixel = (float) (*i); 954 blurredPixel += currentPixel * gPtr[0]; 955 gPtr++; 956 } 957 } 958 *output = (uint8_t) blurredPixel; 959 output ++; 960 } 961 } 962} 963 964 965void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 966 float *gaussian = new float[2 * radius + 1]; 967 computeGaussianWeights(gaussian, radius); 968 969 uint8_t* scratch = new uint8_t[width * height]; 970 971 horizontalBlur(gaussian, radius, image, scratch, width, height); 972 verticalBlur(gaussian, radius, scratch, image, width, height); 973 974 delete[] gaussian; 975 delete[] scratch; 976} 977 978}; // namespace uirenderer 979}; // namespace android 980