rsFont.cpp revision 00237f18a9d31afdca5cca8d621397fbf5b16076
1 2/* 3 * Copyright (C) 2009 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#include "rsContext.h" 19 20#include "rsFont.h" 21#include "rsProgramFragment.h" 22#include <cutils/properties.h> 23 24#include <ft2build.h> 25#include FT_FREETYPE_H 26#include FT_BITMAP_H 27 28#include <GLES/gl.h> 29#include <GLES/glext.h> 30#include <GLES2/gl2.h> 31#include <GLES2/gl2ext.h> 32 33using namespace android; 34using namespace android::renderscript; 35 36Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) { 37 mInitialized = false; 38 mHasKerning = false; 39 mFace = NULL; 40} 41 42bool Font::init(const char *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) { 43 if (mInitialized) { 44 LOGE("Reinitialization of fonts not supported"); 45 return false; 46 } 47 48 FT_Error error = 0; 49 if (data != NULL && dataLen > 0) { 50 error = FT_New_Memory_Face(mRSC->mStateFont.getLib(), (const FT_Byte*)data, dataLen, 0, &mFace); 51 } else { 52 error = FT_New_Face(mRSC->mStateFont.getLib(), name, 0, &mFace); 53 } 54 55 if (error) { 56 LOGE("Unable to initialize font %s", name); 57 return false; 58 } 59 60 mFontName = name; 61 mFontSize = fontSize; 62 mDpi = dpi; 63 64 error = FT_Set_Char_Size(mFace, (FT_F26Dot6)(fontSize * 64.0f), 0, dpi, 0); 65 if (error) { 66 LOGE("Unable to set font size on %s", name); 67 return false; 68 } 69 70 mHasKerning = FT_HAS_KERNING(mFace); 71 72 mInitialized = true; 73 return true; 74} 75 76void Font::preDestroy() const { 77 for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) { 78 if (mRSC->mStateFont.mActiveFonts[ct] == this) { 79 mRSC->mStateFont.mActiveFonts.removeAt(ct); 80 break; 81 } 82 } 83} 84 85void Font::invalidateTextureCache() { 86 for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { 87 mCachedGlyphs.valueAt(i)->mIsValid = false; 88 } 89} 90 91void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y) { 92 FontState *state = &mRSC->mStateFont; 93 94 int32_t nPenX = x + glyph->mBitmapLeft; 95 int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight; 96 97 float u1 = glyph->mBitmapMinU; 98 float u2 = glyph->mBitmapMaxU; 99 float v1 = glyph->mBitmapMinV; 100 float v2 = glyph->mBitmapMaxV; 101 102 int32_t width = (int32_t) glyph->mBitmapWidth; 103 int32_t height = (int32_t) glyph->mBitmapHeight; 104 105 state->appendMeshQuad(nPenX, nPenY, 0, u1, v2, 106 nPenX + width, nPenY, 0, u2, v2, 107 nPenX + width, nPenY - height, 0, u2, v1, 108 nPenX, nPenY - height, 0, u1, v1); 109} 110 111void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int32_t x, int32_t y, 112 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) { 113 int32_t nPenX = x + glyph->mBitmapLeft; 114 int32_t nPenY = y + glyph->mBitmapTop; 115 116 uint32_t endX = glyph->mBitmapMinX + glyph->mBitmapWidth; 117 uint32_t endY = glyph->mBitmapMinY + glyph->mBitmapHeight; 118 119 FontState *state = &mRSC->mStateFont; 120 uint32_t cacheWidth = state->getCacheTextureType()->getDimX(); 121 const uint8_t* cacheBuffer = state->getTextTextureData(); 122 123 uint32_t cacheX = 0, cacheY = 0; 124 int32_t bX = 0, bY = 0; 125 for (cacheX = glyph->mBitmapMinX, bX = nPenX; cacheX < endX; cacheX++, bX++) { 126 for (cacheY = glyph->mBitmapMinY, bY = nPenY; cacheY < endY; cacheY++, bY++) { 127 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { 128 LOGE("Skipping invalid index"); 129 continue; 130 } 131 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; 132 bitmap[bY * bitmapW + bX] = tempCol; 133 } 134 } 135} 136 137void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds) { 138 int32_t nPenX = x + glyph->mBitmapLeft; 139 int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight; 140 141 int32_t width = (int32_t) glyph->mBitmapWidth; 142 int32_t height = (int32_t) glyph->mBitmapHeight; 143 144 // 0, 0 is top left, so bottom is a positive number 145 if (bounds->bottom < nPenY) { 146 bounds->bottom = nPenY; 147 } 148 if (bounds->left > nPenX) { 149 bounds->left = nPenX; 150 } 151 if (bounds->right < nPenX + width) { 152 bounds->right = nPenX + width; 153 } 154 if (bounds->top > nPenY - height) { 155 bounds->top = nPenY - height; 156 } 157} 158 159void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y, 160 uint32_t start, int32_t numGlyphs, 161 RenderMode mode, Rect *bounds, 162 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 163 if (!mInitialized || numGlyphs == 0 || text == NULL || len == 0) { 164 return; 165 } 166 167 if (mode == Font::MEASURE) { 168 if (bounds == NULL) { 169 LOGE("No return rectangle provided to measure text"); 170 return; 171 } 172 // Reset min and max of the bounding box to something large 173 bounds->set(1e6, -1e6, 1e6, -1e6); 174 } 175 176 int32_t penX = x, penY = y; 177 int32_t glyphsLeft = 1; 178 if (numGlyphs > 0) { 179 glyphsLeft = numGlyphs; 180 } 181 182 size_t index = start; 183 size_t nextIndex = 0; 184 185 while (glyphsLeft > 0) { 186 187 int32_t utfChar = utf32_from_utf8_at(text, len, index, &nextIndex); 188 189 // Reached the end of the string or encountered 190 if (utfChar < 0) { 191 break; 192 } 193 194 // Move to the next character in the array 195 index = nextIndex; 196 197 CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar); 198 199 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 200 if (cachedGlyph->mIsValid) { 201 switch (mode) { 202 case FRAMEBUFFER: 203 drawCachedGlyph(cachedGlyph, penX, penY); 204 break; 205 case BITMAP: 206 drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH); 207 break; 208 case MEASURE: 209 measureCachedGlyph(cachedGlyph, penX, penY, bounds); 210 break; 211 } 212 } 213 214 penX += (cachedGlyph->mAdvanceX >> 6); 215 216 // If we were given a specific number of glyphs, decrement 217 if (numGlyphs > 0) { 218 glyphsLeft --; 219 } 220 } 221} 222 223Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) { 224 225 CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar); 226 if (cachedGlyph == NULL) { 227 cachedGlyph = cacheGlyph((uint32_t)utfChar); 228 } 229 // Is the glyph still in texture cache? 230 if (!cachedGlyph->mIsValid) { 231 updateGlyphCache(cachedGlyph); 232 } 233 234 return cachedGlyph; 235} 236 237void Font::updateGlyphCache(CachedGlyphInfo *glyph) { 238 FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER ); 239 if (error) { 240 LOGE("Couldn't load glyph."); 241 return; 242 } 243 244 glyph->mAdvanceX = mFace->glyph->advance.x; 245 glyph->mAdvanceY = mFace->glyph->advance.y; 246 glyph->mBitmapLeft = mFace->glyph->bitmap_left; 247 glyph->mBitmapTop = mFace->glyph->bitmap_top; 248 249 FT_Bitmap *bitmap = &mFace->glyph->bitmap; 250 251 // Now copy the bitmap into the cache texture 252 uint32_t startX = 0; 253 uint32_t startY = 0; 254 255 // Let the font state figure out where to put the bitmap 256 FontState *state = &mRSC->mStateFont; 257 glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY); 258 259 if (!glyph->mIsValid) { 260 return; 261 } 262 263 uint32_t endX = startX + bitmap->width; 264 uint32_t endY = startY + bitmap->rows; 265 266 glyph->mBitmapMinX = startX; 267 glyph->mBitmapMinY = startY; 268 glyph->mBitmapWidth = bitmap->width; 269 glyph->mBitmapHeight = bitmap->rows; 270 271 uint32_t cacheWidth = state->getCacheTextureType()->getDimX(); 272 uint32_t cacheHeight = state->getCacheTextureType()->getDimY(); 273 274 glyph->mBitmapMinU = (float)startX / (float)cacheWidth; 275 glyph->mBitmapMinV = (float)startY / (float)cacheHeight; 276 glyph->mBitmapMaxU = (float)endX / (float)cacheWidth; 277 glyph->mBitmapMaxV = (float)endY / (float)cacheHeight; 278} 279 280Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) { 281 CachedGlyphInfo *newGlyph = new CachedGlyphInfo(); 282 mCachedGlyphs.add(glyph, newGlyph); 283 284 newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph); 285 newGlyph->mIsValid = false; 286 287 updateGlyphCache(newGlyph); 288 289 return newGlyph; 290} 291 292Font * Font::create(Context *rsc, const char *name, float fontSize, uint32_t dpi, 293 const void *data, uint32_t dataLen) { 294 rsc->mStateFont.checkInit(); 295 Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts; 296 297 for (uint32_t i = 0; i < activeFonts.size(); i ++) { 298 Font *ithFont = activeFonts[i]; 299 if (ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) { 300 return ithFont; 301 } 302 } 303 304 Font *newFont = new Font(rsc); 305 bool isInitialized = newFont->init(name, fontSize, dpi, data, dataLen); 306 if (isInitialized) { 307 activeFonts.push(newFont); 308 rsc->mStateFont.precacheLatin(newFont); 309 return newFont; 310 } 311 312 ObjectBase::checkDelete(newFont); 313 return NULL; 314} 315 316Font::~Font() { 317 if (mFace) { 318 FT_Done_Face(mFace); 319 } 320 321 for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { 322 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i); 323 delete glyph; 324 } 325} 326 327FontState::FontState() { 328 mInitialized = false; 329 mMaxNumberOfQuads = 1024; 330 mCurrentQuadIndex = 0; 331 mRSC = NULL; 332 mLibrary = NULL; 333 334 // Get the renderer properties 335 char property[PROPERTY_VALUE_MAX]; 336 337 // Get the gamma 338 float gamma = DEFAULT_TEXT_GAMMA; 339 if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) { 340 gamma = atof(property); 341 } 342 343 // Get the black gamma threshold 344 int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; 345 if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) { 346 blackThreshold = atoi(property); 347 } 348 mBlackThreshold = (float)(blackThreshold) / 255.0f; 349 350 // Get the white gamma threshold 351 int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; 352 if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) { 353 whiteThreshold = atoi(property); 354 } 355 mWhiteThreshold = (float)(whiteThreshold) / 255.0f; 356 357 // Compute the gamma tables 358 mBlackGamma = gamma; 359 mWhiteGamma = 1.0f / gamma; 360 361 setFontColor(0.1f, 0.1f, 0.1f, 1.0f); 362} 363 364FontState::~FontState() { 365 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 366 delete mCacheLines[i]; 367 } 368 369 rsAssert(!mActiveFonts.size()); 370} 371 372FT_Library FontState::getLib() { 373 if (!mLibrary) { 374 FT_Error error = FT_Init_FreeType(&mLibrary); 375 if (error) { 376 LOGE("Unable to initialize freetype"); 377 return NULL; 378 } 379 } 380 381 return mLibrary; 382} 383 384void FontState::init(Context *rsc) { 385 mRSC = rsc; 386} 387 388void FontState::flushAllAndInvalidate() { 389 if (mCurrentQuadIndex != 0) { 390 issueDrawCommand(); 391 mCurrentQuadIndex = 0; 392 } 393 for (uint32_t i = 0; i < mActiveFonts.size(); i ++) { 394 mActiveFonts[i]->invalidateTextureCache(); 395 } 396 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 397 mCacheLines[i]->mCurrentCol = 0; 398 } 399} 400 401bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { 402 // If the glyph is too tall, don't cache it 403 if ((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) { 404 LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); 405 return false; 406 } 407 408 // Now copy the bitmap into the cache texture 409 uint32_t startX = 0; 410 uint32_t startY = 0; 411 412 bool bitmapFit = false; 413 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 414 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); 415 if (bitmapFit) { 416 break; 417 } 418 } 419 420 // If the new glyph didn't fit, flush the state so far and invalidate everything 421 if (!bitmapFit) { 422 flushAllAndInvalidate(); 423 424 // Try to fit it again 425 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 426 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); 427 if (bitmapFit) { 428 break; 429 } 430 } 431 432 // if we still don't fit, something is wrong and we shouldn't draw 433 if (!bitmapFit) { 434 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); 435 return false; 436 } 437 } 438 439 *retOriginX = startX; 440 *retOriginY = startY; 441 442 uint32_t endX = startX + bitmap->width; 443 uint32_t endY = startY + bitmap->rows; 444 445 uint32_t cacheWidth = getCacheTextureType()->getDimX(); 446 447 uint8_t *cacheBuffer = (uint8_t*)mTextTexture->getPtr(); 448 uint8_t *bitmapBuffer = bitmap->buffer; 449 450 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 451 for (cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) { 452 for (cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) { 453 uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX]; 454 cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol; 455 } 456 } 457 458 // This will dirty the texture and the shader so next time 459 // we draw it will upload the data 460 mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT); 461 mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get()); 462 463 // Some debug code 464 /*for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 465 LOGE("Cache Line: H: %u Empty Space: %f", 466 mCacheLines[i]->mMaxHeight, 467 (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f); 468 469 }*/ 470 471 return true; 472} 473 474void FontState::initRenderState() { 475 String8 shaderString("varying vec2 varTex0;\n"); 476 shaderString.append("void main() {\n"); 477 shaderString.append(" lowp vec4 col = UNI_Color;\n"); 478 shaderString.append(" col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n"); 479 shaderString.append(" col.a = pow(col.a, UNI_Gamma);\n"); 480 shaderString.append(" gl_FragColor = col;\n"); 481 shaderString.append("}\n"); 482 483 const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4); 484 const Element *gammaElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1); 485 mRSC->mStateElement.elementBuilderBegin(); 486 mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1); 487 mRSC->mStateElement.elementBuilderAdd(gammaElem, "Gamma", 1); 488 const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC); 489 490 Type *inputType = Type::getType(mRSC, constInput, 1, 0, 0, false, false); 491 492 uint32_t tmp[4]; 493 tmp[0] = RS_PROGRAM_PARAM_CONSTANT; 494 tmp[1] = (uint32_t)inputType; 495 tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE; 496 tmp[3] = RS_TEXTURE_2D; 497 498 mFontShaderFConstant.set(new Allocation(mRSC, inputType, 499 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS)); 500 ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), 501 shaderString.length(), tmp, 4); 502 mFontShaderF.set(pf); 503 mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0); 504 505 Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, 506 RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP); 507 mFontSampler.set(sampler); 508 mFontShaderF->bindSampler(mRSC, 0, sampler); 509 510 ProgramStore *fontStore = new ProgramStore(mRSC, true, true, true, true, 511 false, false, 512 RS_BLEND_SRC_SRC_ALPHA, 513 RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, 514 RS_DEPTH_FUNC_ALWAYS); 515 mFontProgramStore.set(fontStore); 516 mFontProgramStore->init(); 517} 518 519void FontState::initTextTexture() { 520 const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1); 521 522 // We will allocate a texture to initially hold 32 character bitmaps 523 Type *texType = Type::getType(mRSC, alphaElem, 1024, 256, 0, false, false); 524 525 Allocation *cacheAlloc = new Allocation(mRSC, texType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE); 526 mTextTexture.set(cacheAlloc); 527 mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT); 528 529 // Split up our cache texture into lines of certain widths 530 int32_t nextLine = 0; 531 mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0)); 532 nextLine += mCacheLines.top()->mMaxHeight; 533 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); 534 nextLine += mCacheLines.top()->mMaxHeight; 535 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); 536 nextLine += mCacheLines.top()->mMaxHeight; 537 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); 538 nextLine += mCacheLines.top()->mMaxHeight; 539 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); 540 nextLine += mCacheLines.top()->mMaxHeight; 541 mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0)); 542 nextLine += mCacheLines.top()->mMaxHeight; 543 mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0)); 544} 545 546// Avoid having to reallocate memory and render quad by quad 547void FontState::initVertexArrayBuffers() { 548 // Now lets write index data 549 const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); 550 uint32_t numIndicies = mMaxNumberOfQuads * 6; 551 Type *indexType = Type::getType(mRSC, indexElem, numIndicies, 0, 0, false, false); 552 553 Allocation *indexAlloc = new Allocation(mRSC, indexType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_VERTEX); 554 uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr(); 555 556 // Four verts, two triangles , six indices per quad 557 for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) { 558 int32_t i6 = i * 6; 559 int32_t i4 = i * 4; 560 561 indexPtr[i6 + 0] = i4 + 0; 562 indexPtr[i6 + 1] = i4 + 1; 563 indexPtr[i6 + 2] = i4 + 2; 564 565 indexPtr[i6 + 3] = i4 + 0; 566 indexPtr[i6 + 4] = i4 + 2; 567 indexPtr[i6 + 5] = i4 + 3; 568 } 569 570 indexAlloc->deferredUploadToBufferObject(mRSC); 571 mIndexBuffer.set(indexAlloc); 572 573 const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3); 574 const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2); 575 576 mRSC->mStateElement.elementBuilderBegin(); 577 mRSC->mStateElement.elementBuilderAdd(posElem, "position", 1); 578 mRSC->mStateElement.elementBuilderAdd(texElem, "texture0", 1); 579 const Element *vertexDataElem = mRSC->mStateElement.elementBuilderCreate(mRSC); 580 581 Type *vertexDataType = Type::getType(mRSC, vertexDataElem, 582 mMaxNumberOfQuads * 4, 583 0, 0, false, false); 584 585 Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_VERTEX); 586 mTextMeshPtr = (float*)vertexAlloc->getPtr(); 587 588 mVertexArray.set(vertexAlloc); 589} 590 591// We don't want to allocate anything unless we actually draw text 592void FontState::checkInit() { 593 if (mInitialized) { 594 return; 595 } 596 597 initTextTexture(); 598 initRenderState(); 599 600 initVertexArrayBuffers(); 601 602 // We store a string with letters in a rough frequency of occurrence 603 mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq"); 604 mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ"); 605 mLatinPrecache += String8(",.?!()-+@;:`'"); 606 mLatinPrecache += String8("0123456789"); 607 608 mInitialized = true; 609} 610 611void FontState::issueDrawCommand() { 612 Context::PushState ps(mRSC); 613 614 mRSC->setProgramVertex(mRSC->getDefaultProgramVertex()); 615 mRSC->setProgramRaster(mRSC->getDefaultProgramRaster()); 616 mRSC->setProgramFragment(mFontShaderF.get()); 617 mRSC->setProgramStore(mFontProgramStore.get()); 618 619 if (mConstantsDirty) { 620 mFontShaderFConstant->data(mRSC, 0, 0, 1, &mConstants, sizeof(mConstants)); 621 mConstantsDirty = false; 622 } 623 624 if (!mRSC->setupCheck()) { 625 return; 626 } 627 628 float *vtx = (float*)mVertexArray->getPtr(); 629 float *tex = vtx + 3; 630 631 VertexArray::Attrib attribs[2]; 632 attribs[0].set(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "ATTRIB_position"); 633 attribs[1].set(GL_FLOAT, 2, 20, false, (uint32_t)tex, "ATTRIB_texture0"); 634 VertexArray va(attribs, 2); 635 va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache); 636 637 mIndexBuffer->uploadCheck(mRSC); 638 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID()); 639 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0)); 640} 641 642void FontState::appendMeshQuad(float x1, float y1, float z1, 643 float u1, float v1, 644 float x2, float y2, float z2, 645 float u2, float v2, 646 float x3, float y3, float z3, 647 float u3, float v3, 648 float x4, float y4, float z4, 649 float u4, float v4) { 650 const uint32_t vertsPerQuad = 4; 651 const uint32_t floatsPerVert = 5; 652 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 653 654 // Cull things that are off the screen 655 float width = (float)mRSC->getWidth(); 656 float height = (float)mRSC->getHeight(); 657 658 if (x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { 659 return; 660 } 661 662 /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1); 663 LOGE("V1 x: %f y: %f z: %f", x2, y2, z2); 664 LOGE("V2 x: %f y: %f z: %f", x3, y3, z3); 665 LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/ 666 667 (*currentPos++) = x1; 668 (*currentPos++) = y1; 669 (*currentPos++) = z1; 670 (*currentPos++) = u1; 671 (*currentPos++) = v1; 672 673 (*currentPos++) = x2; 674 (*currentPos++) = y2; 675 (*currentPos++) = z2; 676 (*currentPos++) = u2; 677 (*currentPos++) = v2; 678 679 (*currentPos++) = x3; 680 (*currentPos++) = y3; 681 (*currentPos++) = z3; 682 (*currentPos++) = u3; 683 (*currentPos++) = v3; 684 685 (*currentPos++) = x4; 686 (*currentPos++) = y4; 687 (*currentPos++) = z4; 688 (*currentPos++) = u4; 689 (*currentPos++) = v4; 690 691 mCurrentQuadIndex ++; 692 693 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 694 issueDrawCommand(); 695 mCurrentQuadIndex = 0; 696 } 697} 698 699uint32_t FontState::getRemainingCacheCapacity() { 700 uint32_t remainingCapacity = 0; 701 uint32_t totalPixels = 0; 702 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 703 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 704 totalPixels += mCacheLines[i]->mMaxWidth; 705 } 706 remainingCapacity = (remainingCapacity * 100) / totalPixels; 707 return remainingCapacity; 708} 709 710void FontState::precacheLatin(Font *font) { 711 // Remaining capacity is measured in % 712 uint32_t remainingCapacity = getRemainingCacheCapacity(); 713 uint32_t precacheIdx = 0; 714 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 715 font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]); 716 remainingCapacity = getRemainingCacheCapacity(); 717 precacheIdx ++; 718 } 719} 720 721 722void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y, 723 uint32_t startIndex, int32_t numGlyphs, 724 Font::RenderMode mode, 725 Font::Rect *bounds, 726 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 727 checkInit(); 728 729 // Render code here 730 Font *currentFont = mRSC->getFont(); 731 if (!currentFont) { 732 if (!mDefault.get()) { 733 String8 fontsDir("/fonts/DroidSans.ttf"); 734 String8 fullPath(getenv("ANDROID_ROOT")); 735 fullPath += fontsDir; 736 737 mDefault.set(Font::create(mRSC, fullPath.string(), 8, mRSC->getDPI())); 738 } 739 currentFont = mDefault.get(); 740 } 741 if (!currentFont) { 742 LOGE("Unable to initialize any fonts"); 743 return; 744 } 745 746 currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs, 747 mode, bounds, bitmap, bitmapW, bitmapH); 748 749 if (mCurrentQuadIndex != 0) { 750 issueDrawCommand(); 751 mCurrentQuadIndex = 0; 752 } 753} 754 755void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) { 756 renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds); 757 bounds->bottom = - bounds->bottom; 758 bounds->top = - bounds->top; 759} 760 761void FontState::setFontColor(float r, float g, float b, float a) { 762 mConstants.mFontColor[0] = r; 763 mConstants.mFontColor[1] = g; 764 mConstants.mFontColor[2] = b; 765 mConstants.mFontColor[3] = a; 766 767 mConstants.mGamma = 1.0f; 768 const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f; 769 if (luminance <= mBlackThreshold) { 770 mConstants.mGamma = mBlackGamma; 771 } else if (luminance >= mWhiteThreshold) { 772 mConstants.mGamma = mWhiteGamma; 773 } 774 775 mConstantsDirty = true; 776} 777 778void FontState::getFontColor(float *r, float *g, float *b, float *a) const { 779 *r = mConstants.mFontColor[0]; 780 *g = mConstants.mFontColor[1]; 781 *b = mConstants.mFontColor[2]; 782 *a = mConstants.mFontColor[3]; 783} 784 785void FontState::deinit(Context *rsc) { 786 mInitialized = false; 787 788 mFontShaderFConstant.clear(); 789 790 mIndexBuffer.clear(); 791 mVertexArray.clear(); 792 793 mFontShaderF.clear(); 794 mFontSampler.clear(); 795 mFontProgramStore.clear(); 796 797 mTextTexture.clear(); 798 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 799 delete mCacheLines[i]; 800 } 801 mCacheLines.clear(); 802 803 mDefault.clear(); 804 805 if (mLibrary) { 806 FT_Done_FreeType( mLibrary ); 807 mLibrary = NULL; 808 } 809} 810 811bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { 812 if ((uint32_t)bitmap->rows > mMaxHeight) { 813 return false; 814 } 815 816 if (mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) { 817 *retOriginX = mCurrentCol; 818 *retOriginY = mCurrentRow; 819 mCurrentCol += bitmap->width; 820 mDirty = true; 821 return true; 822 } 823 824 return false; 825} 826 827namespace android { 828namespace renderscript { 829 830RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, float fontSize, uint32_t dpi) { 831 Font *newFont = Font::create(rsc, name, fontSize, dpi); 832 if (newFont) { 833 newFont->incUserRef(); 834 } 835 return newFont; 836} 837 838RsFont rsi_FontCreateFromMemory(Context *rsc, char const *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) { 839 Font *newFont = Font::create(rsc, name, fontSize, dpi, data, dataLen); 840 if (newFont) { 841 newFont->incUserRef(); 842 } 843 return newFont; 844} 845 846} // renderscript 847} // android 848