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