FontRenderer.cpp revision 889f8d1403761d5668115ced6cbb3f767cfe966d
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#include <utils/Log.h> 23 24#include "FontRenderer.h" 25 26namespace android { 27namespace uirenderer { 28 29/////////////////////////////////////////////////////////////////////////////// 30// Defines 31/////////////////////////////////////////////////////////////////////////////// 32 33#define DEFAULT_TEXT_CACHE_WIDTH 1024 34#define DEFAULT_TEXT_CACHE_HEIGHT 256 35 36/////////////////////////////////////////////////////////////////////////////// 37// Font 38/////////////////////////////////////////////////////////////////////////////// 39 40Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) : 41 mState(state), mFontId(fontId), mFontSize(fontSize) { 42} 43 44 45Font::~Font() { 46 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { 47 if (mState->mActiveFonts[ct] == this) { 48 mState->mActiveFonts.removeAt(ct); 49 break; 50 } 51 } 52 53 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 54 CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i); 55 delete glyph; 56 } 57} 58 59void Font::invalidateTextureCache() { 60 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 61 mCachedGlyphs.valueAt(i)->mIsValid = false; 62 } 63} 64 65void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) { 66 int nPenX = x + glyph->mBitmapLeft; 67 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; 68 69 float u1 = glyph->mBitmapMinU; 70 float u2 = glyph->mBitmapMaxU; 71 float v1 = glyph->mBitmapMinV; 72 float v2 = glyph->mBitmapMaxV; 73 74 int width = (int) glyph->mBitmapWidth; 75 int height = (int) glyph->mBitmapHeight; 76 77 mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2, 78 nPenX + width, nPenY, 0, u2, v2, 79 nPenX + width, nPenY - height, 0, u2, v1, 80 nPenX, nPenY - height, 0, u1, v1); 81} 82 83Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) { 84 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar); 85 if (cachedGlyph == NULL) { 86 cachedGlyph = cacheGlyph(paint, utfChar); 87 } 88 89 // Is the glyph still in texture cache? 90 if (!cachedGlyph->mIsValid) { 91 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar); 92 updateGlyphCache(paint, skiaGlyph, cachedGlyph); 93 } 94 95 return cachedGlyph; 96} 97 98void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 99 int numGlyphs, int x, int y) { 100 if (numGlyphs == 0 || text == NULL || len == 0) { 101 return; 102 } 103 104 int penX = x, penY = y; 105 int glyphsLeft = 1; 106 if (numGlyphs > 0) { 107 glyphsLeft = numGlyphs; 108 } 109 110 text += start; 111 112 while (glyphsLeft > 0) { 113 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); 114 115 // Reached the end of the string or encountered 116 if (utfChar < 0) { 117 break; 118 } 119 120 CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); 121 122 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 123 if (cachedGlyph->mIsValid) { 124 drawCachedGlyph(cachedGlyph, penX, penY); 125 } 126 127 penX += SkFixedFloor(cachedGlyph->mAdvanceX); 128 129 // If we were given a specific number of glyphs, decrement 130 if (numGlyphs > 0) { 131 glyphsLeft--; 132 } 133 } 134} 135 136void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { 137 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 138 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 139 glyph->mBitmapLeft = skiaGlyph.fLeft; 140 glyph->mBitmapTop = skiaGlyph.fTop; 141 142 uint32_t startX = 0; 143 uint32_t startY = 0; 144 145 // Get the bitmap for the glyph 146 paint->findImage(skiaGlyph); 147 glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY); 148 149 if (!glyph->mIsValid) { 150 return; 151 } 152 153 uint32_t endX = startX + skiaGlyph.fWidth; 154 uint32_t endY = startY + skiaGlyph.fHeight; 155 156 glyph->mBitmapWidth = skiaGlyph.fWidth; 157 glyph->mBitmapHeight = skiaGlyph.fHeight; 158 159 uint32_t cacheWidth = mState->getCacheWidth(); 160 uint32_t cacheHeight = mState->getCacheHeight(); 161 162 glyph->mBitmapMinU = (float) startX / (float) cacheWidth; 163 glyph->mBitmapMinV = (float) startY / (float) cacheHeight; 164 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; 165 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; 166 167 mState->mUploadTexture = true; 168} 169 170Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) { 171 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 172 mCachedGlyphs.add(glyph, newGlyph); 173 174 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); 175 newGlyph->mGlyphIndex = skiaGlyph.fID; 176 newGlyph->mIsValid = false; 177 178 updateGlyphCache(paint, skiaGlyph, newGlyph); 179 180 return newGlyph; 181} 182 183Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) { 184 Vector<Font*> &activeFonts = state->mActiveFonts; 185 186 for (uint32_t i = 0; i < activeFonts.size(); i++) { 187 Font* font = activeFonts[i]; 188 if (font->mFontId == fontId && font->mFontSize == fontSize) { 189 return font; 190 } 191 } 192 193 Font* newFont = new Font(state, fontId, fontSize); 194 activeFonts.push(newFont); 195 return newFont; 196} 197 198/////////////////////////////////////////////////////////////////////////////// 199// FontRenderer 200/////////////////////////////////////////////////////////////////////////////// 201 202FontRenderer::FontRenderer() { 203 LOGD("Creating FontRenderer"); 204 205 mInitialized = false; 206 mMaxNumberOfQuads = 1024; 207 mCurrentQuadIndex = 0; 208 mTextureId = 0; 209 210 mIndexBufferID = 0; 211 212 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 213 mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; 214 215 char property[PROPERTY_VALUE_MAX]; 216 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 217 LOGD(" Setting text cache width to %s pixels", property); 218 mCacheWidth = atoi(property); 219 } else { 220 LOGD(" Using default text cache width of %i pixels", mCacheWidth); 221 } 222 223 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 224 LOGD(" Setting text cache width to %s pixels", property); 225 mCacheHeight = atoi(property); 226 } else { 227 LOGD(" Using default text cache height of %i pixels", mCacheHeight); 228 } 229} 230 231FontRenderer::~FontRenderer() { 232 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 233 delete mCacheLines[i]; 234 } 235 mCacheLines.clear(); 236 237 delete mTextMeshPtr; 238 239 delete mTextTexture; 240 if(mTextureId) { 241 glDeleteTextures(1, &mTextureId); 242 } 243 244 Vector<Font*> fontsToDereference = mActiveFonts; 245 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 246 delete fontsToDereference[i]; 247 } 248} 249 250void FontRenderer::flushAllAndInvalidate() { 251 if (mCurrentQuadIndex != 0) { 252 issueDrawCommand(); 253 mCurrentQuadIndex = 0; 254 } 255 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 256 mActiveFonts[i]->invalidateTextureCache(); 257 } 258 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 259 mCacheLines[i]->mCurrentCol = 0; 260 } 261} 262 263bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { 264 // If the glyph is too tall, don't cache it 265 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 266 LOGE("Font size to large to fit in cache. width, height = %i, %i", 267 (int) glyph.fWidth, (int) glyph.fHeight); 268 return false; 269 } 270 271 // Now copy the bitmap into the cache texture 272 uint32_t startX = 0; 273 uint32_t startY = 0; 274 275 bool bitmapFit = false; 276 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 277 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 278 if (bitmapFit) { 279 break; 280 } 281 } 282 283 // If the new glyph didn't fit, flush the state so far and invalidate everything 284 if (!bitmapFit) { 285 flushAllAndInvalidate(); 286 287 // Try to fit it again 288 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 289 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 290 if (bitmapFit) { 291 break; 292 } 293 } 294 295 // if we still don't fit, something is wrong and we shouldn't draw 296 if (!bitmapFit) { 297 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", 298 (int) glyph.fWidth, (int) glyph.fHeight); 299 return false; 300 } 301 } 302 303 *retOriginX = startX; 304 *retOriginY = startY; 305 306 uint32_t endX = startX + glyph.fWidth; 307 uint32_t endY = startY + glyph.fHeight; 308 309 uint32_t cacheWidth = mCacheWidth; 310 311 unsigned char* cacheBuffer = mTextTexture; 312 unsigned char* bitmapBuffer = (unsigned char*) glyph.fImage; 313 unsigned int stride = glyph.rowBytes(); 314 315 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 316 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 317 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 318 unsigned char tempCol = bitmapBuffer[bY * stride + bX]; 319 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol; 320 } 321 } 322 323 return true; 324} 325 326void FontRenderer::initTextTexture() { 327 mTextTexture = new unsigned char[mCacheWidth * mCacheHeight]; 328 mUploadTexture = false; 329 330 glActiveTexture(GL_TEXTURE0); 331 glGenTextures(1, &mTextureId); 332 glBindTexture(GL_TEXTURE_2D, mTextureId); 333 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 334 // Initialize texture dimentions 335 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, 336 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 337 338 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 339 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 340 341 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 342 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 343 344 // Split up our cache texture into lines of certain widths 345 int nextLine = 0; 346 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0)); 347 nextLine += mCacheLines.top()->mMaxHeight; 348 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 349 nextLine += mCacheLines.top()->mMaxHeight; 350 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 351 nextLine += mCacheLines.top()->mMaxHeight; 352 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 353 nextLine += mCacheLines.top()->mMaxHeight; 354 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 355 nextLine += mCacheLines.top()->mMaxHeight; 356 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0)); 357 nextLine += mCacheLines.top()->mMaxHeight; 358 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); 359} 360 361// Avoid having to reallocate memory and render quad by quad 362void FontRenderer::initVertexArrayBuffers() { 363 uint32_t numIndicies = mMaxNumberOfQuads * 6; 364 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); 365 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 366 367 // Four verts, two triangles , six indices per quad 368 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 369 int i6 = i * 6; 370 int i4 = i * 4; 371 372 indexBufferData[i6 + 0] = i4 + 0; 373 indexBufferData[i6 + 1] = i4 + 1; 374 indexBufferData[i6 + 2] = i4 + 2; 375 376 indexBufferData[i6 + 3] = i4 + 0; 377 indexBufferData[i6 + 4] = i4 + 2; 378 indexBufferData[i6 + 5] = i4 + 3; 379 } 380 381 glGenBuffers(1, &mIndexBufferID); 382 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID); 383 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW); 384 glBindBuffer(GL_ARRAY_BUFFER, 0); 385 386 free(indexBufferData); 387 388 uint32_t coordSize = 3; 389 uint32_t uvSize = 2; 390 uint32_t vertsPerQuad = 4; 391 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 392 mTextMeshPtr = new float[vertexBufferSize]; 393} 394 395// We don't want to allocate anything unless we actually draw text 396void FontRenderer::checkInit() { 397 if (mInitialized) { 398 return; 399 } 400 401 initTextTexture(); 402 initVertexArrayBuffers(); 403 404 // We store a string with letters in a rough frequency of occurrence 405 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); 406 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); 407 mLatinPrecache += String16(",.?!()-+@;:`'"); 408 mLatinPrecache += String16("0123456789"); 409 410 mInitialized = true; 411} 412 413void FontRenderer::checkTextureUpdate() { 414 if (!mUploadTexture) { 415 return; 416 } 417 418 glBindTexture(GL_TEXTURE_2D, mTextureId); 419 420 // Iterate over all the cache lines and see which ones need to be updated 421 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 422 CacheTextureLine* cl = mCacheLines[i]; 423 if(cl->mDirty) { 424 uint32_t xOffset = 0; 425 uint32_t yOffset = cl->mCurrentRow; 426 uint32_t width = mCacheWidth; 427 uint32_t height = cl->mMaxHeight; 428 void* textureData = mTextTexture + yOffset*width; 429 430 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 431 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 432 433 cl->mDirty = false; 434 } 435 } 436 437 mUploadTexture = false; 438} 439 440void FontRenderer::issueDrawCommand() { 441 442 checkTextureUpdate(); 443 444 float* vtx = mTextMeshPtr; 445 float* tex = vtx + 3; 446 447 // position is slot 0 448 uint32_t slot = 0; 449 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx); 450 451 // texture0 is slot 1 452 slot = 1; 453 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex); 454 455 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); 456 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 457} 458 459void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, 460 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, 461 float x4, float y4, float z4, float u4, float v4) { 462 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) { 463 return; 464 } 465 466 const uint32_t vertsPerQuad = 4; 467 const uint32_t floatsPerVert = 5; 468 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 469 470 (*currentPos++) = x1; 471 (*currentPos++) = y1; 472 (*currentPos++) = z1; 473 (*currentPos++) = u1; 474 (*currentPos++) = v1; 475 476 (*currentPos++) = x2; 477 (*currentPos++) = y2; 478 (*currentPos++) = z2; 479 (*currentPos++) = u2; 480 (*currentPos++) = v2; 481 482 (*currentPos++) = x3; 483 (*currentPos++) = y3; 484 (*currentPos++) = z3; 485 (*currentPos++) = u3; 486 (*currentPos++) = v3; 487 488 (*currentPos++) = x4; 489 (*currentPos++) = y4; 490 (*currentPos++) = z4; 491 (*currentPos++) = u4; 492 (*currentPos++) = v4; 493 494 mCurrentQuadIndex++; 495 496 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 497 issueDrawCommand(); 498 mCurrentQuadIndex = 0; 499 } 500} 501 502uint32_t FontRenderer::getRemainingCacheCapacity() { 503 uint32_t remainingCapacity = 0; 504 float totalPixels = 0; 505 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 506 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 507 totalPixels += mCacheLines[i]->mMaxWidth; 508 } 509 remainingCapacity = (remainingCapacity * 100) / totalPixels; 510 return remainingCapacity; 511} 512 513void FontRenderer::precacheLatin(SkPaint* paint) { 514 // Remaining capacity is measured in % 515 uint32_t remainingCapacity = getRemainingCacheCapacity(); 516 uint32_t precacheIdx = 0; 517 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 518 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]); 519 remainingCapacity = getRemainingCacheCapacity(); 520 precacheIdx ++; 521 } 522} 523 524void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 525 uint32_t currentNumFonts = mActiveFonts.size(); 526 mCurrentFont = Font::create(this, fontId, fontSize); 527 528 const float maxPrecacheFontSize = 40.0f; 529 bool isNewFont = currentNumFonts != mActiveFonts.size(); 530 531 if(isNewFont && fontSize <= maxPrecacheFontSize ){ 532 precacheLatin(paint); 533 } 534} 535 536void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 537 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) { 538 checkInit(); 539 540 if (!mCurrentFont) { 541 LOGE("No font set"); 542 return; 543 } 544 545 mClip = clip; 546 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); 547 548 if (mCurrentQuadIndex != 0) { 549 issueDrawCommand(); 550 mCurrentQuadIndex = 0; 551 } 552} 553 554}; // namespace uirenderer 555}; // namespace android 556