FontRenderer.cpp revision 51769a68a5cb34e9564740c6a854fcb93018789d
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 203 mIndexBufferID = 0; 204 205 mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; 206 mCacheHeight = DEFAULT_TEXT_CACHE_WIDTH; 207 208 char property[PROPERTY_VALUE_MAX]; 209 if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { 210 LOGD(" Setting text cache width to %s pixels", property); 211 mCacheWidth = atoi(property); 212 } else { 213 LOGD(" Using default text cache width of %i pixels", mCacheWidth); 214 } 215 216 if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { 217 LOGD(" Setting text cache width to %s pixels", property); 218 mCacheHeight = atoi(property); 219 } else { 220 LOGD(" Using default text cache height of %i pixels", mCacheHeight); 221 } 222} 223 224FontRenderer::~FontRenderer() { 225 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 226 delete mCacheLines[i]; 227 } 228 mCacheLines.clear(); 229 230 delete mTextTexture; 231 232 Vector<Font*> fontsToDereference = mActiveFonts; 233 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 234 delete fontsToDereference[i]; 235 } 236} 237 238void FontRenderer::flushAllAndInvalidate() { 239 if (mCurrentQuadIndex != 0) { 240 issueDrawCommand(); 241 mCurrentQuadIndex = 0; 242 } 243 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 244 mActiveFonts[i]->invalidateTextureCache(); 245 } 246 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 247 mCacheLines[i]->mCurrentCol = 0; 248 } 249} 250 251bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { 252 // If the glyph is too tall, don't cache it 253 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 254 LOGE("Font size to large to fit in cache. width, height = %i, %i", 255 (int) glyph.fWidth, (int) glyph.fHeight); 256 return false; 257 } 258 259 // Now copy the bitmap into the cache texture 260 uint32_t startX = 0; 261 uint32_t startY = 0; 262 263 bool bitmapFit = false; 264 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 265 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 266 if (bitmapFit) { 267 break; 268 } 269 } 270 271 // If the new glyph didn't fit, flush the state so far and invalidate everything 272 if (!bitmapFit) { 273 flushAllAndInvalidate(); 274 275 // Try to fit it again 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 we still don't fit, something is wrong and we shouldn't draw 284 if (!bitmapFit) { 285 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", 286 (int) glyph.fWidth, (int) glyph.fHeight); 287 return false; 288 } 289 } 290 291 *retOriginX = startX; 292 *retOriginY = startY; 293 294 uint32_t endX = startX + glyph.fWidth; 295 uint32_t endY = startY + glyph.fHeight; 296 297 uint32_t cacheWidth = mCacheWidth; 298 299 unsigned char* cacheBuffer = mTextTexture; 300 unsigned char* bitmapBuffer = (unsigned char*) glyph.fImage; 301 unsigned int stride = glyph.rowBytes(); 302 303 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 304 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 305 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 306 unsigned char tempCol = bitmapBuffer[bY * stride + bX]; 307 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol; 308 } 309 } 310 311 return true; 312} 313 314void FontRenderer::initTextTexture() { 315 mTextTexture = new unsigned char[mCacheWidth * mCacheHeight]; 316 mUploadTexture = false; 317 318 glGenTextures(1, &mTextureId); 319 glBindTexture(GL_TEXTURE_2D, mTextureId); 320 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 321 322 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 323 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 324 325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 326 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 327 328 // Split up our cache texture into lines of certain widths 329 int nextLine = 0; 330 mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0)); 331 nextLine += mCacheLines.top()->mMaxHeight; 332 mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0)); 333 nextLine += mCacheLines.top()->mMaxHeight; 334 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 335 nextLine += mCacheLines.top()->mMaxHeight; 336 mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0)); 337 nextLine += mCacheLines.top()->mMaxHeight; 338 mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0)); 339 nextLine += mCacheLines.top()->mMaxHeight; 340 mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); 341} 342 343// Avoid having to reallocate memory and render quad by quad 344void FontRenderer::initVertexArrayBuffers() { 345 uint32_t numIndicies = mMaxNumberOfQuads * 6; 346 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); 347 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 348 349 // Four verts, two triangles , six indices per quad 350 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 351 int i6 = i * 6; 352 int i4 = i * 4; 353 354 indexBufferData[i6 + 0] = i4 + 0; 355 indexBufferData[i6 + 1] = i4 + 1; 356 indexBufferData[i6 + 2] = i4 + 2; 357 358 indexBufferData[i6 + 3] = i4 + 0; 359 indexBufferData[i6 + 4] = i4 + 2; 360 indexBufferData[i6 + 5] = i4 + 3; 361 } 362 363 glGenBuffers(1, &mIndexBufferID); 364 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID); 365 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW); 366 glBindBuffer(GL_ARRAY_BUFFER, 0); 367 368 free(indexBufferData); 369 370 uint32_t coordSize = 3; 371 uint32_t uvSize = 2; 372 uint32_t vertsPerQuad = 4; 373 uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize * 374 uvSize * sizeof(float); 375 mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes); 376} 377 378// We don't want to allocate anything unless we actually draw text 379void FontRenderer::checkInit() { 380 if (mInitialized) { 381 return; 382 } 383 384 initTextTexture(); 385 initVertexArrayBuffers(); 386 387 mInitialized = true; 388} 389 390void FontRenderer::issueDrawCommand() { 391 if (mUploadTexture) { 392 glBindTexture(GL_TEXTURE_2D, mTextureId); 393 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA, 394 GL_UNSIGNED_BYTE, mTextTexture); 395 mUploadTexture = false; 396 } 397 398 float* vtx = mTextMeshPtr; 399 float* tex = vtx + 3; 400 401 // position is slot 0 402 uint32_t slot = 0; 403 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx); 404 405 // texture0 is slot 1 406 slot = 1; 407 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex); 408 409 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); 410 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 411} 412 413void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, 414 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, 415 float x4, float y4, float z4, float u4, float v4) { 416 if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) { 417 return; 418 } 419 420 const uint32_t vertsPerQuad = 4; 421 const uint32_t floatsPerVert = 5; 422 float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 423 424 (*currentPos++) = x1; 425 (*currentPos++) = y1; 426 (*currentPos++) = z1; 427 (*currentPos++) = u1; 428 (*currentPos++) = v1; 429 430 (*currentPos++) = x2; 431 (*currentPos++) = y2; 432 (*currentPos++) = z2; 433 (*currentPos++) = u2; 434 (*currentPos++) = v2; 435 436 (*currentPos++) = x3; 437 (*currentPos++) = y3; 438 (*currentPos++) = z3; 439 (*currentPos++) = u3; 440 (*currentPos++) = v3; 441 442 (*currentPos++) = x4; 443 (*currentPos++) = y4; 444 (*currentPos++) = z4; 445 (*currentPos++) = u4; 446 (*currentPos++) = v4; 447 448 mCurrentQuadIndex++; 449 450 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 451 issueDrawCommand(); 452 mCurrentQuadIndex = 0; 453 } 454} 455 456void FontRenderer::setFont(uint32_t fontId, float fontSize) { 457 mCurrentFont = Font::create(this, fontId, fontSize); 458} 459 460void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, 461 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) { 462 checkInit(); 463 464 if (!mCurrentFont) { 465 LOGE("No font set"); 466 return; 467 } 468 469 mClip = clip; 470 mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); 471 472 if (mCurrentQuadIndex != 0) { 473 issueDrawCommand(); 474 mCurrentQuadIndex = 0; 475 } 476} 477 478}; // namespace uirenderer 479}; // namespace android 480