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