FontRenderer.cpp revision 65ef909776c03417d8b597738da54ca211e37e4f
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 glGenTextures(1, &mTextureId); 331 glBindTexture(GL_TEXTURE_2D, mTextureId); 332 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 333 // Initialize texture dimentions 334 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, 335 GL_ALPHA, GL_UNSIGNED_BYTE, 0); 336 337 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 338 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 339 340 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 341 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 342 343 // Split up our cache texture into lines of certain widths 344 int nextLine = 0; 345 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0)); 346 nextLine += mCacheLines.top()->mMaxHeight; 347 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 348 nextLine += mCacheLines.top()->mMaxHeight; 349 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 350 nextLine += mCacheLines.top()->mMaxHeight; 351 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 352 nextLine += mCacheLines.top()->mMaxHeight; 353 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 354 nextLine += mCacheLines.top()->mMaxHeight; 355 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0)); 356 nextLine += mCacheLines.top()->mMaxHeight; 357 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); 358} 359 360// Avoid having to reallocate memory and render quad by quad 361void FontRenderer::initVertexArrayBuffers() { 362 uint32_t numIndicies = mMaxNumberOfQuads * 6; 363 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); 364 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 365 366 // Four verts, two triangles , six indices per quad 367 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 368 int i6 = i * 6; 369 int i4 = i * 4; 370 371 indexBufferData[i6 + 0] = i4 + 0; 372 indexBufferData[i6 + 1] = i4 + 1; 373 indexBufferData[i6 + 2] = i4 + 2; 374 375 indexBufferData[i6 + 3] = i4 + 0; 376 indexBufferData[i6 + 4] = i4 + 2; 377 indexBufferData[i6 + 5] = i4 + 3; 378 } 379 380 glGenBuffers(1, &mIndexBufferID); 381 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID); 382 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW); 383 glBindBuffer(GL_ARRAY_BUFFER, 0); 384 385 free(indexBufferData); 386 387 uint32_t coordSize = 3; 388 uint32_t uvSize = 2; 389 uint32_t vertsPerQuad = 4; 390 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 391 mTextMeshPtr = new float[vertexBufferSize]; 392} 393 394// We don't want to allocate anything unless we actually draw text 395void FontRenderer::checkInit() { 396 if (mInitialized) { 397 return; 398 } 399 400 initTextTexture(); 401 initVertexArrayBuffers(); 402 403 // We store a string with letters in a rough frequency of occurrence 404 mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); 405 mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); 406 mLatinPrecache += String16(",.?!()-+@;:`'"); 407 mLatinPrecache += String16("0123456789"); 408 409 mInitialized = true; 410} 411 412void FontRenderer::checkTextureUpdate() { 413 if (!mUploadTexture) { 414 return; 415 } 416 417 glBindTexture(GL_TEXTURE_2D, mTextureId); 418 419 // Iterate over all the cache lines and see which ones need to be updated 420 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 421 CacheTextureLine* cl = mCacheLines[i]; 422 if(cl->mDirty) { 423 uint32_t xOffset = 0; 424 uint32_t yOffset = cl->mCurrentRow; 425 uint32_t width = mCacheWidth; 426 uint32_t height = cl->mMaxHeight; 427 void* textureData = mTextTexture + yOffset*width; 428 429 glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, 430 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 431 432 cl->mDirty = false; 433 } 434 } 435 436 mUploadTexture = false; 437} 438 439void FontRenderer::issueDrawCommand() { 440 441 checkTextureUpdate(); 442 443 float* vtx = mTextMeshPtr; 444 float* tex = vtx + 3; 445 446 // position is slot 0 447 uint32_t slot = 0; 448 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx); 449 450 // texture0 is slot 1 451 slot = 1; 452 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex); 453 454 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); 455 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 456} 457 458void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, 459 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, 460 float x4, float y4, float z4, float u4, float v4) { 461 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) { 462 return; 463 } 464 465 const uint32_t vertsPerQuad = 4; 466 const uint32_t floatsPerVert = 5; 467 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 468 469 (*currentPos++) = x1; 470 (*currentPos++) = y1; 471 (*currentPos++) = z1; 472 (*currentPos++) = u1; 473 (*currentPos++) = v1; 474 475 (*currentPos++) = x2; 476 (*currentPos++) = y2; 477 (*currentPos++) = z2; 478 (*currentPos++) = u2; 479 (*currentPos++) = v2; 480 481 (*currentPos++) = x3; 482 (*currentPos++) = y3; 483 (*currentPos++) = z3; 484 (*currentPos++) = u3; 485 (*currentPos++) = v3; 486 487 (*currentPos++) = x4; 488 (*currentPos++) = y4; 489 (*currentPos++) = z4; 490 (*currentPos++) = u4; 491 (*currentPos++) = v4; 492 493 mCurrentQuadIndex++; 494 495 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 496 issueDrawCommand(); 497 mCurrentQuadIndex = 0; 498 } 499} 500 501uint32_t FontRenderer::getRemainingCacheCapacity() { 502 uint32_t remainingCapacity = 0; 503 float totalPixels = 0; 504 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 505 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 506 totalPixels += mCacheLines[i]->mMaxWidth; 507 } 508 remainingCapacity = (remainingCapacity * 100) / totalPixels; 509 return remainingCapacity; 510} 511 512void FontRenderer::precacheLatin(SkPaint* paint) { 513 // Remaining capacity is measured in % 514 uint32_t remainingCapacity = getRemainingCacheCapacity(); 515 uint32_t precacheIdx = 0; 516 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 517 mCurrentFont->getCachedUTFChar(paint, (int32_t)mLatinPrecache[precacheIdx]); 518 remainingCapacity = getRemainingCacheCapacity(); 519 precacheIdx ++; 520 } 521} 522 523void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { 524 uint32_t currentNumFonts = mActiveFonts.size(); 525 mCurrentFont = Font::create(this, fontId, fontSize); 526 527 const float maxPrecacheFontSize = 40.0f; 528 bool isNewFont = currentNumFonts != mActiveFonts.size(); 529 530 if(isNewFont && fontSize <= maxPrecacheFontSize ){ 531 precacheLatin(paint); 532 } 533} 534 535void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 536 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) { 537 checkInit(); 538 539 if (!mCurrentFont) { 540 LOGE("No font set"); 541 return; 542 } 543 544 mClip = clip; 545 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); 546 547 if (mCurrentQuadIndex != 0) { 548 issueDrawCommand(); 549 mCurrentQuadIndex = 0; 550 } 551} 552 553}; // namespace uirenderer 554}; // namespace android 555