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