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