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