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