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 ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4); 494 ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1); 495 Element::Builder builder; 496 builder.add(colorElem.get(), "Color", 1); 497 builder.add(gammaElem.get(), "Gamma", 1); 498 ObjectBaseRef<const Element> constInput = builder.create(mRSC); 499 500 ObjectBaseRef<Type> inputType = Type::getTypeRef(mRSC, constInput.get(), 1, 0, 0, false, false); 501 502 uint32_t tmp[4]; 503 tmp[0] = RS_PROGRAM_PARAM_CONSTANT; 504 tmp[1] = (uint32_t)inputType.get(); 505 tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE; 506 tmp[3] = RS_TEXTURE_2D; 507 508 mFontShaderFConstant.set(Allocation::createAllocation(mRSC, inputType.get(), 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 mFontSampler.set(Sampler::getSampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, 516 RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP).get()); 517 mFontShaderF->bindSampler(mRSC, 0, mFontSampler.get()); 518 519 mFontProgramStore.set(ProgramStore::getProgramStore(mRSC, true, true, true, true, 520 false, false, 521 RS_BLEND_SRC_SRC_ALPHA, 522 RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, 523 RS_DEPTH_FUNC_ALWAYS).get()); 524 mFontProgramStore->init(); 525} 526 527void FontState::initTextTexture() { 528 ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1); 529 530 // We will allocate a texture to initially hold 32 character bitmaps 531 ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(), 1024, 256, 0, false, false); 532 533 Allocation *cacheAlloc = Allocation::createAllocation(mRSC, texType.get(), 534 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE); 535 mTextTexture.set(cacheAlloc); 536 mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT); 537 538 // Split up our cache texture into lines of certain widths 539 int32_t nextLine = 0; 540 mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0)); 541 nextLine += mCacheLines.top()->mMaxHeight; 542 mCacheLines.push(new CacheTextureLine(24, 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(32, 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(40, texType->getDimX(), nextLine, 0)); 551 nextLine += mCacheLines.top()->mMaxHeight; 552 mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0)); 553} 554 555// Avoid having to reallocate memory and render quad by quad 556void FontState::initVertexArrayBuffers() { 557 // Now lets write index data 558 ObjectBaseRef<const Element> indexElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); 559 uint32_t numIndicies = mMaxNumberOfQuads * 6; 560 ObjectBaseRef<Type> indexType = Type::getTypeRef(mRSC, indexElem.get(), numIndicies, 0, 0, false, false); 561 562 Allocation *indexAlloc = Allocation::createAllocation(mRSC, indexType.get(), 563 RS_ALLOCATION_USAGE_SCRIPT | 564 RS_ALLOCATION_USAGE_GRAPHICS_VERTEX); 565 uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr(); 566 567 // Four verts, two triangles , six indices per quad 568 for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) { 569 int32_t i6 = i * 6; 570 int32_t i4 = i * 4; 571 572 indexPtr[i6 + 0] = i4 + 0; 573 indexPtr[i6 + 1] = i4 + 1; 574 indexPtr[i6 + 2] = i4 + 2; 575 576 indexPtr[i6 + 3] = i4 + 0; 577 indexPtr[i6 + 4] = i4 + 2; 578 indexPtr[i6 + 5] = i4 + 3; 579 } 580 581 indexAlloc->sendDirty(mRSC); 582 583 ObjectBaseRef<const Element> posElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3); 584 ObjectBaseRef<const Element> texElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2); 585 586 Element::Builder builder; 587 builder.add(posElem.get(), "position", 1); 588 builder.add(texElem.get(), "texture0", 1); 589 ObjectBaseRef<const Element> vertexDataElem = builder.create(mRSC); 590 591 ObjectBaseRef<Type> vertexDataType = Type::getTypeRef(mRSC, vertexDataElem.get(), 592 mMaxNumberOfQuads * 4, 593 0, 0, false, false); 594 595 Allocation *vertexAlloc = Allocation::createAllocation(mRSC, vertexDataType.get(), 596 RS_ALLOCATION_USAGE_SCRIPT); 597 mTextMeshPtr = (float*)vertexAlloc->getPtr(); 598 599 mMesh.set(new Mesh(mRSC, 1, 1)); 600 mMesh->setVertexBuffer(vertexAlloc, 0); 601 mMesh->setPrimitive(indexAlloc, RS_PRIMITIVE_TRIANGLE, 0); 602 mMesh->init(); 603} 604 605// We don't want to allocate anything unless we actually draw text 606void FontState::checkInit() { 607 if (mInitialized) { 608 return; 609 } 610 611 initTextTexture(); 612 initRenderState(); 613 614 initVertexArrayBuffers(); 615 616 // We store a string with letters in a rough frequency of occurrence 617 mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq"); 618 mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ"); 619 mLatinPrecache += String8(",.?!()-+@;:`'"); 620 mLatinPrecache += String8("0123456789"); 621 622 mInitialized = true; 623} 624 625void FontState::issueDrawCommand() { 626 Context::PushState ps(mRSC); 627 628 mRSC->setProgramVertex(mRSC->getDefaultProgramVertex()); 629 mRSC->setProgramRaster(mRSC->getDefaultProgramRaster()); 630 mRSC->setProgramFragment(mFontShaderF.get()); 631 mRSC->setProgramStore(mFontProgramStore.get()); 632 633 if (mConstantsDirty) { 634 mFontShaderFConstant->data(mRSC, 0, 0, 1, &mConstants, sizeof(mConstants)); 635 mConstantsDirty = false; 636 } 637 638 if (!mRSC->setupCheck()) { 639 return; 640 } 641 642 mMesh->renderPrimitiveRange(mRSC, 0, 0, mCurrentQuadIndex * 6); 643} 644 645void FontState::appendMeshQuad(float x1, float y1, float z1, 646 float u1, float v1, 647 float x2, float y2, float z2, 648 float u2, float v2, 649 float x3, float y3, float z3, 650 float u3, float v3, 651 float x4, float y4, float z4, 652 float u4, float v4) { 653 const uint32_t vertsPerQuad = 4; 654 const uint32_t floatsPerVert = 5; 655 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 656 657 // Cull things that are off the screen 658 float width = (float)mRSC->getWidth(); 659 float height = (float)mRSC->getHeight(); 660 661 if (x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { 662 return; 663 } 664 665 /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1); 666 LOGE("V1 x: %f y: %f z: %f", x2, y2, z2); 667 LOGE("V2 x: %f y: %f z: %f", x3, y3, z3); 668 LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/ 669 670 (*currentPos++) = x1; 671 (*currentPos++) = y1; 672 (*currentPos++) = z1; 673 (*currentPos++) = u1; 674 (*currentPos++) = v1; 675 676 (*currentPos++) = x2; 677 (*currentPos++) = y2; 678 (*currentPos++) = z2; 679 (*currentPos++) = u2; 680 (*currentPos++) = v2; 681 682 (*currentPos++) = x3; 683 (*currentPos++) = y3; 684 (*currentPos++) = z3; 685 (*currentPos++) = u3; 686 (*currentPos++) = v3; 687 688 (*currentPos++) = x4; 689 (*currentPos++) = y4; 690 (*currentPos++) = z4; 691 (*currentPos++) = u4; 692 (*currentPos++) = v4; 693 694 mCurrentQuadIndex ++; 695 696 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 697 issueDrawCommand(); 698 mCurrentQuadIndex = 0; 699 } 700} 701 702uint32_t FontState::getRemainingCacheCapacity() { 703 uint32_t remainingCapacity = 0; 704 uint32_t totalPixels = 0; 705 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 706 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 707 totalPixels += mCacheLines[i]->mMaxWidth; 708 } 709 remainingCapacity = (remainingCapacity * 100) / totalPixels; 710 return remainingCapacity; 711} 712 713void FontState::precacheLatin(Font *font) { 714 // Remaining capacity is measured in % 715 uint32_t remainingCapacity = getRemainingCacheCapacity(); 716 uint32_t precacheIdx = 0; 717 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 718 font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]); 719 remainingCapacity = getRemainingCacheCapacity(); 720 precacheIdx ++; 721 } 722} 723 724 725void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y, 726 uint32_t startIndex, int32_t numGlyphs, 727 Font::RenderMode mode, 728 Font::Rect *bounds, 729 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 730 checkInit(); 731 732 // Render code here 733 Font *currentFont = mRSC->getFont(); 734 if (!currentFont) { 735 if (!mDefault.get()) { 736 String8 fontsDir("/fonts/Roboto-Regular.ttf"); 737 String8 fullPath(getenv("ANDROID_ROOT")); 738 fullPath += fontsDir; 739 740 mDefault.set(Font::create(mRSC, fullPath.string(), 8, mRSC->getDPI())); 741 } 742 currentFont = mDefault.get(); 743 } 744 if (!currentFont) { 745 LOGE("Unable to initialize any fonts"); 746 return; 747 } 748 749 currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs, 750 mode, bounds, bitmap, bitmapW, bitmapH); 751 752 if (mCurrentQuadIndex != 0) { 753 issueDrawCommand(); 754 mCurrentQuadIndex = 0; 755 } 756} 757 758void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) { 759 renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds); 760 bounds->bottom = - bounds->bottom; 761 bounds->top = - bounds->top; 762} 763 764void FontState::setFontColor(float r, float g, float b, float a) { 765 mConstants.mFontColor[0] = r; 766 mConstants.mFontColor[1] = g; 767 mConstants.mFontColor[2] = b; 768 mConstants.mFontColor[3] = a; 769 770 mConstants.mGamma = 1.0f; 771 const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f; 772 if (luminance <= mBlackThreshold) { 773 mConstants.mGamma = mBlackGamma; 774 } else if (luminance >= mWhiteThreshold) { 775 mConstants.mGamma = mWhiteGamma; 776 } 777 778 mConstantsDirty = true; 779} 780 781void FontState::getFontColor(float *r, float *g, float *b, float *a) const { 782 *r = mConstants.mFontColor[0]; 783 *g = mConstants.mFontColor[1]; 784 *b = mConstants.mFontColor[2]; 785 *a = mConstants.mFontColor[3]; 786} 787 788void FontState::deinit(Context *rsc) { 789 mInitialized = false; 790 791 mFontShaderFConstant.clear(); 792 793 mMesh.clear(); 794 795 mFontShaderF.clear(); 796 mFontSampler.clear(); 797 mFontProgramStore.clear(); 798 799 mTextTexture.clear(); 800 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 801 delete mCacheLines[i]; 802 } 803 mCacheLines.clear(); 804 805 mDefault.clear(); 806#ifndef ANDROID_RS_SERIALIZE 807 if (mLibrary) { 808 FT_Done_FreeType( mLibrary ); 809 mLibrary = NULL; 810 } 811#endif //ANDROID_RS_SERIALIZE 812} 813 814#ifndef ANDROID_RS_SERIALIZE 815bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { 816 if ((uint32_t)bitmap->rows > mMaxHeight) { 817 return false; 818 } 819 820 if (mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) { 821 *retOriginX = mCurrentCol; 822 *retOriginY = mCurrentRow; 823 mCurrentCol += bitmap->width; 824 mDirty = true; 825 return true; 826 } 827 828 return false; 829} 830#endif //ANDROID_RS_SERIALIZE 831 832namespace android { 833namespace renderscript { 834 835RsFont rsi_FontCreateFromFile(Context *rsc, 836 char const *name, size_t name_length, 837 float fontSize, uint32_t dpi) { 838 Font *newFont = Font::create(rsc, name, fontSize, dpi); 839 if (newFont) { 840 newFont->incUserRef(); 841 } 842 return newFont; 843} 844 845RsFont rsi_FontCreateFromMemory(Context *rsc, 846 char const *name, size_t name_length, 847 float fontSize, uint32_t dpi, 848 const void *data, size_t data_length) { 849 Font *newFont = Font::create(rsc, name, fontSize, dpi, data, data_length); 850 if (newFont) { 851 newFont->incUserRef(); 852 } 853 return newFont; 854} 855 856} // renderscript 857} // android 858