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