FontRenderer.cpp revision 694b519ac647fe998fd396fe0784cc8e179aadc4
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 "FontRenderer.h" 20 21#include <SkUtils.h> 22 23namespace android { 24namespace uirenderer { 25 26/////////////////////////////////////////////////////////////////////////////// 27// Font 28/////////////////////////////////////////////////////////////////////////////// 29 30Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) : 31 mState(state), mFontId(fontId), mFontSize(fontSize) { 32} 33 34 35Font::~Font() { 36 for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { 37 if (mState->mActiveFonts[ct] == this) { 38 mState->mActiveFonts.removeAt(ct); 39 break; 40 } 41 } 42 43 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 44 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i); 45 delete glyph; 46 } 47} 48 49void Font::invalidateTextureCache() { 50 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 51 mCachedGlyphs.valueAt(i)->mIsValid = false; 52 } 53} 54 55void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) { 56 FontRenderer *state = mState; 57 58 int nPenX = x + glyph->mBitmapLeft; 59 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; 60 61 state->appendMeshQuad(nPenX, nPenY, 0, glyph->mBitmapMinU, glyph->mBitmapMaxV, 62 nPenX + (int) glyph->mBitmapWidth, nPenY, 0, glyph->mBitmapMaxU, glyph->mBitmapMaxV, 63 nPenX + (int) glyph->mBitmapWidth, nPenY - (int) glyph->mBitmapHeight, 64 0, glyph->mBitmapMaxU, glyph->mBitmapMinV, nPenX, nPenY - (int) glyph->mBitmapHeight, 65 0, glyph->mBitmapMinU, glyph->mBitmapMinV); 66} 67 68void Font::renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start, int numGlyphs, 69 int x, int y) { 70 if (numGlyphs == 0 || text == NULL || len == 0) { 71 return; 72 } 73 74 int penX = x, penY = y; 75 int glyphsLeft = 1; 76 if (numGlyphs > 0) { 77 glyphsLeft = numGlyphs; 78 } 79 80 //size_t index = start; 81 //size_t nextIndex = 0; 82 83 text += start; 84 85 while (glyphsLeft > 0) { 86 //int32_t utfChar = utf32_at(text, len, index, &nextIndex); 87 int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); 88 89 // Reached the end of the string or encountered 90 if (utfChar < 0) { 91 break; 92 } 93 94 // Move to the next character in the array 95 //index = nextIndex; 96 97 CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor(utfChar); 98 99 if (cachedGlyph == NULL) { 100 cachedGlyph = cacheGlyph(paint, utfChar); 101 } 102 // Is the glyph still in texture cache? 103 if (!cachedGlyph->mIsValid) { 104 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar); 105 updateGlyphCache(paint, skiaGlyph, cachedGlyph); 106 } 107 108 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 109 if (cachedGlyph->mIsValid) { 110 drawCachedGlyph(cachedGlyph, penX, penY); 111 } 112 113 // TODO: Check how to do this conversion 114 penX += SkFixedRound(cachedGlyph->mAdvanceX); 115 116 // If we were given a specific number of glyphs, decrement 117 if (numGlyphs > 0) { 118 glyphsLeft--; 119 } 120 } 121} 122 123void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph) { 124 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 125 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 126 glyph->mBitmapLeft = skiaGlyph.fLeft; 127 glyph->mBitmapTop = skiaGlyph.fTop; 128 129 uint32_t startX = 0; 130 uint32_t startY = 0; 131 132 // Let the font state figure out where to put the bitmap 133 FontRenderer *state = mState; 134 // Get the bitmap for the glyph 135 paint->findImage(skiaGlyph); 136 glyph->mIsValid = state->cacheBitmap(skiaGlyph, &startX, &startY); 137 138 if (!glyph->mIsValid) { 139 return; 140 } 141 142 uint32_t endX = startX + skiaGlyph.fWidth; 143 uint32_t endY = startY + skiaGlyph.fHeight; 144 145 glyph->mBitmapWidth = skiaGlyph.fWidth; 146 glyph->mBitmapHeight = skiaGlyph.fHeight; 147 148 uint32_t cacheWidth = state->getCacheWidth(); 149 uint32_t cacheHeight = state->getCacheHeight(); 150 151 glyph->mBitmapMinU = (float) startX / (float) cacheWidth; 152 glyph->mBitmapMinV = (float) startY / (float) cacheHeight; 153 glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; 154 glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; 155 156 state->mUploadTexture = true; 157} 158 159Font::CachedGlyphInfo *Font::cacheGlyph(SkPaint* paint, int32_t glyph) { 160 CachedGlyphInfo *newGlyph = new CachedGlyphInfo(); 161 mCachedGlyphs.add(glyph, newGlyph); 162 163 const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); 164 newGlyph->mGlyphIndex = skiaGlyph.fID; 165 newGlyph->mIsValid = false; 166 167 updateGlyphCache(paint, skiaGlyph, newGlyph); 168 169 return newGlyph; 170} 171 172Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) { 173 Vector<Font*> &activeFonts = state->mActiveFonts; 174 175 for (uint32_t i = 0; i < activeFonts.size(); i++) { 176 Font *ithFont = activeFonts[i]; 177 if (ithFont->mFontId == fontId && ithFont->mFontSize == fontSize) { 178 return ithFont; 179 } 180 } 181 182 Font* newFont = new Font(state, fontId, fontSize); 183 activeFonts.push(newFont); 184 return newFont; 185} 186 187/////////////////////////////////////////////////////////////////////////////// 188// FontRenderer 189/////////////////////////////////////////////////////////////////////////////// 190 191FontRenderer::FontRenderer() { 192 mInitialized = false; 193 mMaxNumberOfQuads = 1024; 194 mCurrentQuadIndex = 0; 195 196 mIndexBufferID = 0; 197 198 mCacheWidth = 1024; 199 mCacheHeight = 256; 200} 201 202FontRenderer::~FontRenderer() { 203 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 204 delete mCacheLines[i]; 205 } 206 mCacheLines.clear(); 207 208 delete mTextTexture; 209 210 Vector<Font*> fontsToDereference = mActiveFonts; 211 for (uint32_t i = 0; i < fontsToDereference.size(); i++) { 212 delete fontsToDereference[i]; 213 } 214} 215 216void FontRenderer::flushAllAndInvalidate() { 217 if (mCurrentQuadIndex != 0) { 218 issueDrawCommand(); 219 mCurrentQuadIndex = 0; 220 } 221 for (uint32_t i = 0; i < mActiveFonts.size(); i++) { 222 mActiveFonts[i]->invalidateTextureCache(); 223 } 224 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 225 mCacheLines[i]->mCurrentCol = 0; 226 } 227} 228 229bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { 230 // If the glyph is too tall, don't cache it 231 if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { 232 LOGE("Font size to large to fit in cache. width, height = %i, %i", 233 (int) glyph.fWidth, (int) glyph.fHeight); 234 return false; 235 } 236 237 // Now copy the bitmap into the cache texture 238 uint32_t startX = 0; 239 uint32_t startY = 0; 240 241 bool bitmapFit = false; 242 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 243 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 244 if (bitmapFit) { 245 break; 246 } 247 } 248 249 // If the new glyph didn't fit, flush the state so far and invalidate everything 250 if (!bitmapFit) { 251 flushAllAndInvalidate(); 252 253 // Try to fit it again 254 for (uint32_t i = 0; i < mCacheLines.size(); i++) { 255 bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); 256 if (bitmapFit) { 257 break; 258 } 259 } 260 261 // if we still don't fit, something is wrong and we shouldn't draw 262 if (!bitmapFit) { 263 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", 264 (int) glyph.fWidth, (int) glyph.fHeight); 265 return false; 266 } 267 } 268 269 *retOriginX = startX; 270 *retOriginY = startY; 271 272 uint32_t endX = startX + glyph.fWidth; 273 uint32_t endY = startY + glyph.fHeight; 274 275 uint32_t cacheWidth = mCacheWidth; 276 277 unsigned char *cacheBuffer = mTextTexture; 278 unsigned char *bitmapBuffer = (unsigned char*) glyph.fImage; 279 unsigned int stride = glyph.rowBytes(); 280 281 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 282 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 283 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { 284 unsigned char tempCol = bitmapBuffer[bY * stride + bX]; 285 cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol; 286 } 287 } 288 289 return true; 290} 291 292void FontRenderer::initTextTexture() { 293 mTextTexture = new unsigned char[mCacheWidth * mCacheHeight]; 294 mUploadTexture = false; 295 296 glGenTextures(1, &mTextureId); 297 glBindTexture(GL_TEXTURE_2D, mTextureId); 298 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 299 300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 302 303 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 304 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 305 306 // Split up our cache texture into lines of certain widths 307 int nextLine = 0; 308 mCacheLines.push(new CacheTextureLine(16, mCacheWidth, nextLine, 0)); 309 nextLine += mCacheLines.top()->mMaxHeight; 310 mCacheLines.push(new CacheTextureLine(24, mCacheWidth, nextLine, 0)); 311 nextLine += mCacheLines.top()->mMaxHeight; 312 mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0)); 313 nextLine += mCacheLines.top()->mMaxHeight; 314 mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0)); 315 nextLine += mCacheLines.top()->mMaxHeight; 316 mCacheLines.push(new CacheTextureLine(40, mCacheWidth, nextLine, 0)); 317 nextLine += mCacheLines.top()->mMaxHeight; 318 mCacheLines.push(new CacheTextureLine(mCacheHeight - nextLine, mCacheWidth, nextLine, 0)); 319} 320 321// Avoid having to reallocate memory and render quad by quad 322void FontRenderer::initVertexArrayBuffers() { 323 uint32_t numIndicies = mMaxNumberOfQuads * 6; 324 uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); 325 uint16_t *indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 326 327 // Four verts, two triangles , six indices per quad 328 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 329 int i6 = i * 6; 330 int i4 = i * 4; 331 332 indexBufferData[i6 + 0] = i4 + 0; 333 indexBufferData[i6 + 1] = i4 + 1; 334 indexBufferData[i6 + 2] = i4 + 2; 335 336 indexBufferData[i6 + 3] = i4 + 0; 337 indexBufferData[i6 + 4] = i4 + 2; 338 indexBufferData[i6 + 5] = i4 + 3; 339 } 340 341 glGenBuffers(1, &mIndexBufferID); 342 glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID); 343 glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW); 344 glBindBuffer(GL_ARRAY_BUFFER, 0); 345 346 free(indexBufferData); 347 348 uint32_t coordSize = 3; 349 uint32_t uvSize = 2; 350 uint32_t vertsPerQuad = 4; 351 uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize * 352 uvSize * sizeof(float); 353 mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes); 354} 355 356// We don't want to allocate anything unless we actually draw text 357void FontRenderer::checkInit() { 358 if (mInitialized) { 359 return; 360 } 361 362 initTextTexture(); 363 initVertexArrayBuffers(); 364 365 mInitialized = true; 366} 367 368void FontRenderer::issueDrawCommand() { 369 if (mUploadTexture) { 370 glBindTexture(GL_TEXTURE_2D, mTextureId); 371 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA, 372 GL_UNSIGNED_BYTE, mTextTexture); 373 mUploadTexture = false; 374 } 375 376 float *vtx = mTextMeshPtr; 377 float *tex = vtx + 3; 378 379 // position is slot 0 380 uint32_t slot = 0; 381 glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx); 382 383 // texture0 is slot 1 384 slot = 1; 385 glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex); 386 387 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); 388 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); 389} 390 391void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, 392 float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, 393 float x4, float y4, float z4, float u4, float v4) { 394 const uint32_t vertsPerQuad = 4; 395 const uint32_t floatsPerVert = 5; 396 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 397 398 // TODO: Cull things that are off the screen 399 // float width = (float)mRSC->getWidth(); 400 // float height = (float)mRSC->getHeight(); 401 // 402 // if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { 403 // return; 404 // } 405 406 (*currentPos++) = x1; 407 (*currentPos++) = y1; 408 (*currentPos++) = z1; 409 (*currentPos++) = u1; 410 (*currentPos++) = v1; 411 412 (*currentPos++) = x2; 413 (*currentPos++) = y2; 414 (*currentPos++) = z2; 415 (*currentPos++) = u2; 416 (*currentPos++) = v2; 417 418 (*currentPos++) = x3; 419 (*currentPos++) = y3; 420 (*currentPos++) = z3; 421 (*currentPos++) = u3; 422 (*currentPos++) = v3; 423 424 (*currentPos++) = x4; 425 (*currentPos++) = y4; 426 (*currentPos++) = z4; 427 (*currentPos++) = u4; 428 (*currentPos++) = v4; 429 430 mCurrentQuadIndex++; 431 432 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 433 issueDrawCommand(); 434 mCurrentQuadIndex = 0; 435 } 436} 437 438void FontRenderer::setFont(uint32_t fontId, float fontSize) { 439 mCurrentFont = Font::create(this, fontId, fontSize); 440} 441 442void FontRenderer::renderText(SkPaint* paint, const char *text, uint32_t len, uint32_t startIndex, 443 int numGlyphs, int x, int y) { 444 checkInit(); 445 446 // Render code here 447 Font *currentFont = mCurrentFont; 448 if (!currentFont) { 449 LOGE("Unable to initialize any fonts"); 450 return; 451 } 452 453 currentFont->renderUTF(paint, text, len, startIndex, numGlyphs, x, y); 454 455 if (mCurrentQuadIndex != 0) { 456 issueDrawCommand(); 457 mCurrentQuadIndex = 0; 458 } 459} 460 461void FontRenderer::renderText(SkPaint* paint, const char *text, int x, int y) { 462 size_t textLen = strlen(text); 463 renderText(paint, text, textLen, 0, -1, x, y); 464} 465 466}; // namespace uirenderer 467}; // namespace android 468