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