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