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