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