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 void 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 180 void 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 235 void 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 273 Font::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 286 Font* 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 311 static bool sLogFontRendererCreate = true; 312 313 FontRenderer::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 360 FontRenderer::~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 381 void 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 394 bool 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 465 void 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 521 void FontRenderer::initVertexArrayBuffers() { 522 uint32_t numIndicies = mMaxNumberOfQuads * 6; 523 uint32_t indexBufferSizeBytes = numIndicies * 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 = 3; 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 555 void 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 572 void 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 599 void FontRenderer::issueDrawCommand() { 600 checkTextureUpdate(); 601 602 float* vtx = mTextMeshPtr; 603 float* tex = vtx + 3; 604 605 glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx); 606 glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, 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 614 void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, 615 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, 616 float x4, float y4, float z4, float u4, float v4) { 617 if (mClip && 618 (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 mClip = NULL; 727 mBounds = NULL; 728 729 Rect bounds; 730 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds); 731 732 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 733 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 734 uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; 735 736 for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { 737 dataBuffer[i] = 0; 738 } 739 740 int penX = radius - bounds.left; 741 int penY = radius - bounds.bottom; 742 743 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 744 dataBuffer, paddedWidth, paddedHeight); 745 blurImage(dataBuffer, paddedWidth, paddedHeight, radius); 746 747 DropShadow image; 748 image.width = paddedWidth; 749 image.height = paddedHeight; 750 image.image = dataBuffer; 751 image.penX = penX; 752 image.penY = penY; 753 return image; 754 } 755 756 bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 757 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { 758 checkInit(); 759 760 if (!mCurrentFont) { 761 LOGE("No font set"); 762 return false; 763 } 764 765 if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) { 766 LOGE("Font renderer unable to draw, attribute slots undefined"); 767 return false; 768 } 769 770 mDrawn = false; 771 mBounds = bounds; 772 mClip = clip; 773 774 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); 775 776 mBounds = NULL; 777 mClip = NULL; 778 779 if (mCurrentQuadIndex != 0) { 780 issueDrawCommand(); 781 mCurrentQuadIndex = 0; 782 } 783 784 return mDrawn; 785 } 786 787 void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 788 // Compute gaussian weights for the blur 789 // e is the euler's number 790 float e = 2.718281828459045f; 791 float pi = 3.1415926535897932f; 792 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 793 // x is of the form [-radius .. 0 .. radius] 794 // and sigma varies with radius. 795 // Based on some experimental radius values and sigma's 796 // we approximately fit sigma = f(radius) as 797 // sigma = radius * 0.3 + 0.6 798 // The larger the radius gets, the more our gaussian blur 799 // will resemble a box blur since with large sigma 800 // the gaussian curve begins to lose its shape 801 float sigma = 0.3f * (float) radius + 0.6f; 802 803 // Now compute the coefficints 804 // We will store some redundant values to save some math during 805 // the blur calculations 806 // precompute some values 807 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 808 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 809 810 float normalizeFactor = 0.0f; 811 for (int32_t r = -radius; r <= radius; r ++) { 812 float floatR = (float) r; 813 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 814 normalizeFactor += weights[r + radius]; 815 } 816 817 //Now we need to normalize the weights because all our coefficients need to add up to one 818 normalizeFactor = 1.0f / normalizeFactor; 819 for (int32_t r = -radius; r <= radius; r ++) { 820 weights[r + radius] *= normalizeFactor; 821 } 822 } 823 824 void FontRenderer::horizontalBlur(float* weights, int32_t radius, 825 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 826 float blurredPixel = 0.0f; 827 float currentPixel = 0.0f; 828 829 for (int32_t y = 0; y < height; y ++) { 830 831 const uint8_t* input = source + y * width; 832 uint8_t* output = dest + y * width; 833 834 for (int32_t x = 0; x < width; x ++) { 835 blurredPixel = 0.0f; 836 const float* gPtr = weights; 837 // Optimization for non-border pixels 838 if (x > radius && x < (width - radius)) { 839 const uint8_t *i = input + (x - radius); 840 for (int r = -radius; r <= radius; r ++) { 841 currentPixel = (float) (*i); 842 blurredPixel += currentPixel * gPtr[0]; 843 gPtr++; 844 i++; 845 } 846 } else { 847 for (int32_t r = -radius; r <= radius; r ++) { 848 // Stepping left and right away from the pixel 849 int validW = x + r; 850 if (validW < 0) { 851 validW = 0; 852 } 853 if (validW > width - 1) { 854 validW = width - 1; 855 } 856 857 currentPixel = (float) input[validW]; 858 blurredPixel += currentPixel * gPtr[0]; 859 gPtr++; 860 } 861 } 862 *output = (uint8_t)blurredPixel; 863 output ++; 864 } 865 } 866 } 867 868 void FontRenderer::verticalBlur(float* weights, int32_t radius, 869 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 870 float blurredPixel = 0.0f; 871 float currentPixel = 0.0f; 872 873 for (int32_t y = 0; y < height; y ++) { 874 875 uint8_t* output = dest + y * width; 876 877 for (int32_t x = 0; x < width; x ++) { 878 blurredPixel = 0.0f; 879 const float* gPtr = weights; 880 const uint8_t* input = source + x; 881 // Optimization for non-border pixels 882 if (y > radius && y < (height - radius)) { 883 const uint8_t *i = input + ((y - radius) * width); 884 for (int32_t r = -radius; r <= radius; r ++) { 885 currentPixel = (float)(*i); 886 blurredPixel += currentPixel * gPtr[0]; 887 gPtr++; 888 i += width; 889 } 890 } else { 891 for (int32_t r = -radius; r <= radius; r ++) { 892 int validH = y + r; 893 // Clamp to zero and width 894 if (validH < 0) { 895 validH = 0; 896 } 897 if (validH > height - 1) { 898 validH = height - 1; 899 } 900 901 const uint8_t *i = input + validH * width; 902 currentPixel = (float) (*i); 903 blurredPixel += currentPixel * gPtr[0]; 904 gPtr++; 905 } 906 } 907 *output = (uint8_t) blurredPixel; 908 output ++; 909 } 910 } 911 } 912 913 914 void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { 915 float *gaussian = new float[2 * radius + 1]; 916 computeGaussianWeights(gaussian, radius); 917 uint8_t* scratch = new uint8_t[width * height]; 918 horizontalBlur(gaussian, radius, image, scratch, width, height); 919 verticalBlur(gaussian, radius, scratch, image, width, height); 920 delete[] gaussian; 921 delete[] scratch; 922 } 923 924 }; // namespace uirenderer 925 }; // namespace android 926