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#include "rs.h" 20#include "rsFont.h" 21#include "rsProgramFragment.h" 22#include "rsMesh.h" 23#include <cutils/properties.h> 24 25#ifndef ANDROID_RS_SERIALIZE 26#include <ft2build.h> 27#include FT_FREETYPE_H 28#include FT_BITMAP_H 29#endif //ANDROID_RS_SERIALIZE 30 31using namespace android; 32using namespace android::renderscript; 33 34Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) { 35 mInitialized = false; 36 mHasKerning = false; 37 mFace = NULL; 38} 39 40bool Font::init(const char *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) { 41#ifndef ANDROID_RS_SERIALIZE 42 if (mInitialized) { 43 ALOGE("Reinitialization of fonts not supported"); 44 return false; 45 } 46 47 FT_Error error = 0; 48 if (data != NULL && dataLen > 0) { 49 error = FT_New_Memory_Face(mRSC->mStateFont.getLib(), (const FT_Byte*)data, dataLen, 0, &mFace); 50 } else { 51 error = FT_New_Face(mRSC->mStateFont.getLib(), name, 0, &mFace); 52 } 53 54 if (error) { 55 ALOGE("Unable to initialize font %s", name); 56 return false; 57 } 58 59 mFontName = name; 60 mFontSize = fontSize; 61 mDpi = dpi; 62 63 error = FT_Set_Char_Size(mFace, (FT_F26Dot6)(fontSize * 64.0f), 0, dpi, 0); 64 if (error) { 65 ALOGE("Unable to set font size on %s", name); 66 return false; 67 } 68 69 mHasKerning = FT_HAS_KERNING(mFace); 70 71 mInitialized = true; 72#endif //ANDROID_RS_SERIALIZE 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->mCacheBuffer; 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 ALOGE("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 ALOGE("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#ifndef ANDROID_RS_SERIALIZE 239 FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER ); 240 if (error) { 241 ALOGE("Couldn't load glyph."); 242 return; 243 } 244 245 glyph->mAdvanceX = mFace->glyph->advance.x; 246 glyph->mAdvanceY = mFace->glyph->advance.y; 247 glyph->mBitmapLeft = mFace->glyph->bitmap_left; 248 glyph->mBitmapTop = mFace->glyph->bitmap_top; 249 250 FT_Bitmap *bitmap = &mFace->glyph->bitmap; 251 252 // Now copy the bitmap into the cache texture 253 uint32_t startX = 0; 254 uint32_t startY = 0; 255 256 // Let the font state figure out where to put the bitmap 257 FontState *state = &mRSC->mStateFont; 258 glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY); 259 260 if (!glyph->mIsValid) { 261 return; 262 } 263 264 uint32_t endX = startX + bitmap->width; 265 uint32_t endY = startY + bitmap->rows; 266 267 glyph->mBitmapMinX = startX; 268 glyph->mBitmapMinY = startY; 269 glyph->mBitmapWidth = bitmap->width; 270 glyph->mBitmapHeight = bitmap->rows; 271 272 uint32_t cacheWidth = state->getCacheTextureType()->getDimX(); 273 uint32_t cacheHeight = state->getCacheTextureType()->getDimY(); 274 275 glyph->mBitmapMinU = (float)startX / (float)cacheWidth; 276 glyph->mBitmapMinV = (float)startY / (float)cacheHeight; 277 glyph->mBitmapMaxU = (float)endX / (float)cacheWidth; 278 glyph->mBitmapMaxV = (float)endY / (float)cacheHeight; 279#endif //ANDROID_RS_SERIALIZE 280} 281 282Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) { 283 CachedGlyphInfo *newGlyph = new CachedGlyphInfo(); 284 mCachedGlyphs.add(glyph, newGlyph); 285#ifndef ANDROID_RS_SERIALIZE 286 newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph); 287 newGlyph->mIsValid = false; 288#endif //ANDROID_RS_SERIALIZE 289 updateGlyphCache(newGlyph); 290 291 return newGlyph; 292} 293 294Font * Font::create(Context *rsc, const char *name, float fontSize, uint32_t dpi, 295 const void *data, uint32_t dataLen) { 296 rsc->mStateFont.checkInit(); 297 Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts; 298 299 for (uint32_t i = 0; i < activeFonts.size(); i ++) { 300 Font *ithFont = activeFonts[i]; 301 if (ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) { 302 return ithFont; 303 } 304 } 305 306 Font *newFont = new Font(rsc); 307 bool isInitialized = newFont->init(name, fontSize, dpi, data, dataLen); 308 if (isInitialized) { 309 activeFonts.push(newFont); 310 rsc->mStateFont.precacheLatin(newFont); 311 return newFont; 312 } 313 314 ObjectBase::checkDelete(newFont); 315 return NULL; 316} 317 318Font::~Font() { 319#ifndef ANDROID_RS_SERIALIZE 320 if (mFace) { 321 FT_Done_Face(mFace); 322 } 323#endif 324 325 for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { 326 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i); 327 delete glyph; 328 } 329} 330 331FontState::FontState() { 332 mInitialized = false; 333 mMaxNumberOfQuads = 1024; 334 mCurrentQuadIndex = 0; 335 mRSC = NULL; 336#ifndef ANDROID_RS_SERIALIZE 337 mLibrary = NULL; 338#endif //ANDROID_RS_SERIALIZE 339 340 // Get the renderer properties 341 char property[PROPERTY_VALUE_MAX]; 342 343 // Get the gamma 344 float gamma = DEFAULT_TEXT_GAMMA; 345 if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) { 346 gamma = atof(property); 347 } 348 349 // Get the black gamma threshold 350 int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; 351 if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) { 352 blackThreshold = atoi(property); 353 } 354 mBlackThreshold = (float)(blackThreshold) / 255.0f; 355 356 // Get the white gamma threshold 357 int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; 358 if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) { 359 whiteThreshold = atoi(property); 360 } 361 mWhiteThreshold = (float)(whiteThreshold) / 255.0f; 362 363 // Compute the gamma tables 364 mBlackGamma = gamma; 365 mWhiteGamma = 1.0f / gamma; 366 367 setFontColor(0.1f, 0.1f, 0.1f, 1.0f); 368} 369 370FontState::~FontState() { 371 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 372 delete mCacheLines[i]; 373 } 374 375 rsAssert(!mActiveFonts.size()); 376} 377#ifndef ANDROID_RS_SERIALIZE 378FT_Library FontState::getLib() { 379 if (!mLibrary) { 380 FT_Error error = FT_Init_FreeType(&mLibrary); 381 if (error) { 382 ALOGE("Unable to initialize freetype"); 383 return NULL; 384 } 385 } 386 387 return mLibrary; 388} 389#endif //ANDROID_RS_SERIALIZE 390 391 392void FontState::init(Context *rsc) { 393 mRSC = rsc; 394} 395 396void FontState::flushAllAndInvalidate() { 397 if (mCurrentQuadIndex != 0) { 398 issueDrawCommand(); 399 mCurrentQuadIndex = 0; 400 } 401 for (uint32_t i = 0; i < mActiveFonts.size(); i ++) { 402 mActiveFonts[i]->invalidateTextureCache(); 403 } 404 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 405 mCacheLines[i]->mCurrentCol = 0; 406 } 407} 408 409#ifndef ANDROID_RS_SERIALIZE 410bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { 411 // If the glyph is too tall, don't cache it 412 if ((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) { 413 ALOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); 414 return false; 415 } 416 417 // Now copy the bitmap into the cache texture 418 uint32_t startX = 0; 419 uint32_t startY = 0; 420 421 bool bitmapFit = false; 422 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 423 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); 424 if (bitmapFit) { 425 break; 426 } 427 } 428 429 // If the new glyph didn't fit, flush the state so far and invalidate everything 430 if (!bitmapFit) { 431 flushAllAndInvalidate(); 432 433 // Try to fit it again 434 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 435 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); 436 if (bitmapFit) { 437 break; 438 } 439 } 440 441 // if we still don't fit, something is wrong and we shouldn't draw 442 if (!bitmapFit) { 443 ALOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); 444 return false; 445 } 446 } 447 448 *retOriginX = startX; 449 *retOriginY = startY; 450 451 uint32_t endX = startX + bitmap->width; 452 uint32_t endY = startY + bitmap->rows; 453 454 uint32_t cacheWidth = getCacheTextureType()->getDimX(); 455 456 uint8_t *cacheBuffer = mCacheBuffer; 457 uint8_t *bitmapBuffer = bitmap->buffer; 458 459 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 460 for (cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) { 461 for (cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) { 462 uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX]; 463 cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol; 464 } 465 } 466 467 // This will dirty the texture and the shader so next time 468 // we draw it will upload the data 469 470 mRSC->mHal.funcs.allocation.data2D(mRSC, mTextTexture.get(), 0, 0, 0, 471 RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, mCacheWidth, mCacheHeight, 472 mCacheBuffer, mCacheWidth*mCacheHeight); 473 474 mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get()); 475 476 // Some debug code 477 /*for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 478 ALOGE("Cache Line: H: %u Empty Space: %f", 479 mCacheLines[i]->mMaxHeight, 480 (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f); 481 482 }*/ 483 484 return true; 485} 486#endif //ANDROID_RS_SERIALIZE 487 488void FontState::initRenderState() { 489 String8 shaderString("varying vec2 varTex0;\n"); 490 shaderString.append("void main() {\n"); 491 shaderString.append(" lowp vec4 col = UNI_Color;\n"); 492 shaderString.append(" col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n"); 493 shaderString.append(" col.a = pow(col.a, UNI_Gamma);\n"); 494 shaderString.append(" gl_FragColor = col;\n"); 495 shaderString.append("}\n"); 496 497 const char *textureNames[] = { "Tex0" }; 498 const size_t textureNamesLengths[] = { 4 }; 499 size_t numTextures = sizeof(textureNamesLengths)/sizeof(*textureNamesLengths); 500 501 ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, 502 RS_KIND_USER, false, 4); 503 ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, 504 RS_KIND_USER, false, 1); 505 Element::Builder builder; 506 builder.add(colorElem.get(), "Color", 1); 507 builder.add(gammaElem.get(), "Gamma", 1); 508 ObjectBaseRef<const Element> constInput = builder.create(mRSC); 509 510 ObjectBaseRef<Type> inputType = Type::getTypeRef(mRSC, constInput.get(), 1, 0, 0, false, false); 511 512 uint32_t tmp[4]; 513 tmp[0] = RS_PROGRAM_PARAM_CONSTANT; 514 tmp[1] = (uint32_t)inputType.get(); 515 tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE; 516 tmp[3] = RS_TEXTURE_2D; 517 518 mFontShaderFConstant.set(Allocation::createAllocation(mRSC, inputType.get(), 519 RS_ALLOCATION_USAGE_SCRIPT | 520 RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS)); 521 ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), shaderString.length(), 522 textureNames, numTextures, textureNamesLengths, 523 tmp, 4); 524 mFontShaderF.set(pf); 525 mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0); 526 527 mFontSampler.set(Sampler::getSampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, 528 RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, 529 RS_SAMPLER_CLAMP).get()); 530 mFontShaderF->bindSampler(mRSC, 0, mFontSampler.get()); 531 532 mFontProgramStore.set(ProgramStore::getProgramStore(mRSC, true, true, true, true, 533 false, false, 534 RS_BLEND_SRC_SRC_ALPHA, 535 RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, 536 RS_DEPTH_FUNC_ALWAYS).get()); 537 mFontProgramStore->init(); 538} 539 540void FontState::initTextTexture() { 541 ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8, 542 RS_KIND_PIXEL_A, true, 1); 543 544 // We will allocate a texture to initially hold 32 character bitmaps 545 mCacheHeight = 256; 546 mCacheWidth = 1024; 547 ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(), 548 mCacheWidth, mCacheHeight, 0, false, false); 549 mCacheBuffer = new uint8_t[mCacheWidth * mCacheHeight]; 550 551 552 Allocation *cacheAlloc = Allocation::createAllocation(mRSC, texType.get(), 553 RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE); 554 mTextTexture.set(cacheAlloc); 555 556 // Split up our cache texture into lines of certain widths 557 int32_t nextLine = 0; 558 mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0)); 559 nextLine += mCacheLines.top()->mMaxHeight; 560 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); 561 nextLine += mCacheLines.top()->mMaxHeight; 562 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); 563 nextLine += mCacheLines.top()->mMaxHeight; 564 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); 565 nextLine += mCacheLines.top()->mMaxHeight; 566 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); 567 nextLine += mCacheLines.top()->mMaxHeight; 568 mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0)); 569 nextLine += mCacheLines.top()->mMaxHeight; 570 mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0)); 571} 572 573// Avoid having to reallocate memory and render quad by quad 574void FontState::initVertexArrayBuffers() { 575 // Now lets write index data 576 ObjectBaseRef<const Element> indexElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); 577 uint32_t numIndicies = mMaxNumberOfQuads * 6; 578 ObjectBaseRef<Type> indexType = Type::getTypeRef(mRSC, indexElem.get(), numIndicies, 0, 0, false, false); 579 580 Allocation *indexAlloc = Allocation::createAllocation(mRSC, indexType.get(), 581 RS_ALLOCATION_USAGE_SCRIPT | 582 RS_ALLOCATION_USAGE_GRAPHICS_VERTEX); 583 uint16_t *indexPtr = (uint16_t*)mRSC->mHal.funcs.allocation.lock1D(mRSC, indexAlloc); 584 585 // Four verts, two triangles , six indices per quad 586 for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) { 587 int32_t i6 = i * 6; 588 int32_t i4 = i * 4; 589 590 indexPtr[i6 + 0] = i4 + 0; 591 indexPtr[i6 + 1] = i4 + 1; 592 indexPtr[i6 + 2] = i4 + 2; 593 594 indexPtr[i6 + 3] = i4 + 0; 595 indexPtr[i6 + 4] = i4 + 2; 596 indexPtr[i6 + 5] = i4 + 3; 597 } 598 599 indexAlloc->sendDirty(mRSC); 600 601 ObjectBaseRef<const Element> posElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3); 602 ObjectBaseRef<const Element> texElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2); 603 604 Element::Builder builder; 605 builder.add(posElem.get(), "position", 1); 606 builder.add(texElem.get(), "texture0", 1); 607 ObjectBaseRef<const Element> vertexDataElem = builder.create(mRSC); 608 609 ObjectBaseRef<Type> vertexDataType = Type::getTypeRef(mRSC, vertexDataElem.get(), 610 mMaxNumberOfQuads * 4, 611 0, 0, false, false); 612 613 Allocation *vertexAlloc = Allocation::createAllocation(mRSC, vertexDataType.get(), 614 RS_ALLOCATION_USAGE_SCRIPT); 615 mTextMeshPtr = (float*)mRSC->mHal.funcs.allocation.lock1D(mRSC, vertexAlloc); 616 617 mMesh.set(new Mesh(mRSC, 1, 1)); 618 mMesh->setVertexBuffer(vertexAlloc, 0); 619 mMesh->setPrimitive(indexAlloc, RS_PRIMITIVE_TRIANGLE, 0); 620 mMesh->init(); 621 mRSC->mHal.funcs.allocation.unlock1D(mRSC, indexAlloc); 622 mRSC->mHal.funcs.allocation.unlock1D(mRSC, vertexAlloc); 623} 624 625// We don't want to allocate anything unless we actually draw text 626void FontState::checkInit() { 627 if (mInitialized) { 628 return; 629 } 630 631 initTextTexture(); 632 initRenderState(); 633 634 initVertexArrayBuffers(); 635 636 // We store a string with letters in a rough frequency of occurrence 637 mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq"); 638 mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ"); 639 mLatinPrecache += String8(",.?!()-+@;:`'"); 640 mLatinPrecache += String8("0123456789"); 641 642 mInitialized = true; 643} 644 645void FontState::issueDrawCommand() { 646 Context::PushState ps(mRSC); 647 648 mRSC->setProgramVertex(mRSC->getDefaultProgramVertex()); 649 mRSC->setProgramRaster(mRSC->getDefaultProgramRaster()); 650 mRSC->setProgramFragment(mFontShaderF.get()); 651 mRSC->setProgramStore(mFontProgramStore.get()); 652 653 if (mConstantsDirty) { 654 mFontShaderFConstant->data(mRSC, 0, 0, 1, &mConstants, sizeof(mConstants)); 655 mConstantsDirty = false; 656 } 657 658 if (!mRSC->setupCheck()) { 659 return; 660 } 661 662 mMesh->renderPrimitiveRange(mRSC, 0, 0, mCurrentQuadIndex * 6); 663} 664 665void FontState::appendMeshQuad(float x1, float y1, float z1, 666 float u1, float v1, 667 float x2, float y2, float z2, 668 float u2, float v2, 669 float x3, float y3, float z3, 670 float u3, float v3, 671 float x4, float y4, float z4, 672 float u4, float v4) { 673 const uint32_t vertsPerQuad = 4; 674 const uint32_t floatsPerVert = 6; 675 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 676 677 if (x1 > mSurfaceWidth || y1 < 0.0f || x2 < 0 || y4 > mSurfaceHeight) { 678 return; 679 } 680 681 /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1); 682 ALOGE("V1 x: %f y: %f z: %f", x2, y2, z2); 683 ALOGE("V2 x: %f y: %f z: %f", x3, y3, z3); 684 ALOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/ 685 686 (*currentPos++) = x1; 687 (*currentPos++) = y1; 688 (*currentPos++) = z1; 689 (*currentPos++) = 0; 690 (*currentPos++) = u1; 691 (*currentPos++) = v1; 692 693 (*currentPos++) = x2; 694 (*currentPos++) = y2; 695 (*currentPos++) = z2; 696 (*currentPos++) = 0; 697 (*currentPos++) = u2; 698 (*currentPos++) = v2; 699 700 (*currentPos++) = x3; 701 (*currentPos++) = y3; 702 (*currentPos++) = z3; 703 (*currentPos++) = 0; 704 (*currentPos++) = u3; 705 (*currentPos++) = v3; 706 707 (*currentPos++) = x4; 708 (*currentPos++) = y4; 709 (*currentPos++) = z4; 710 (*currentPos++) = 0; 711 (*currentPos++) = u4; 712 (*currentPos++) = v4; 713 714 mCurrentQuadIndex ++; 715 716 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 717 issueDrawCommand(); 718 mCurrentQuadIndex = 0; 719 } 720} 721 722uint32_t FontState::getRemainingCacheCapacity() { 723 uint32_t remainingCapacity = 0; 724 uint32_t totalPixels = 0; 725 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 726 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); 727 totalPixels += mCacheLines[i]->mMaxWidth; 728 } 729 remainingCapacity = (remainingCapacity * 100) / totalPixels; 730 return remainingCapacity; 731} 732 733void FontState::precacheLatin(Font *font) { 734 // Remaining capacity is measured in % 735 uint32_t remainingCapacity = getRemainingCacheCapacity(); 736 uint32_t precacheIdx = 0; 737 while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { 738 font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]); 739 remainingCapacity = getRemainingCacheCapacity(); 740 precacheIdx ++; 741 } 742} 743 744 745void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y, 746 uint32_t startIndex, int32_t numGlyphs, 747 Font::RenderMode mode, 748 Font::Rect *bounds, 749 uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { 750 checkInit(); 751 752 // Render code here 753 Font *currentFont = mRSC->getFont(); 754 if (!currentFont) { 755 if (!mDefault.get()) { 756 String8 fontsDir("/fonts/Roboto-Regular.ttf"); 757 String8 fullPath(getenv("ANDROID_ROOT")); 758 fullPath += fontsDir; 759 760 mDefault.set(Font::create(mRSC, fullPath.string(), 8, mRSC->getDPI())); 761 } 762 currentFont = mDefault.get(); 763 } 764 if (!currentFont) { 765 ALOGE("Unable to initialize any fonts"); 766 return; 767 } 768 769 // Cull things that are off the screen 770 mSurfaceWidth = (float)mRSC->getCurrentSurfaceWidth(); 771 mSurfaceHeight = (float)mRSC->getCurrentSurfaceHeight(); 772 773 currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs, 774 mode, bounds, bitmap, bitmapW, bitmapH); 775 776 if (mCurrentQuadIndex != 0) { 777 issueDrawCommand(); 778 mCurrentQuadIndex = 0; 779 } 780} 781 782void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) { 783 renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds); 784 bounds->bottom = - bounds->bottom; 785 bounds->top = - bounds->top; 786} 787 788void FontState::setFontColor(float r, float g, float b, float a) { 789 mConstants.mFontColor[0] = r; 790 mConstants.mFontColor[1] = g; 791 mConstants.mFontColor[2] = b; 792 mConstants.mFontColor[3] = a; 793 794 mConstants.mGamma = 1.0f; 795 const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f; 796 if (luminance <= mBlackThreshold) { 797 mConstants.mGamma = mBlackGamma; 798 } else if (luminance >= mWhiteThreshold) { 799 mConstants.mGamma = mWhiteGamma; 800 } 801 802 mConstantsDirty = true; 803} 804 805void FontState::getFontColor(float *r, float *g, float *b, float *a) const { 806 *r = mConstants.mFontColor[0]; 807 *g = mConstants.mFontColor[1]; 808 *b = mConstants.mFontColor[2]; 809 *a = mConstants.mFontColor[3]; 810} 811 812void FontState::deinit(Context *rsc) { 813 mInitialized = false; 814 815 mFontShaderFConstant.clear(); 816 817 mMesh.clear(); 818 819 mFontShaderF.clear(); 820 mFontSampler.clear(); 821 mFontProgramStore.clear(); 822 823 mTextTexture.clear(); 824 for (uint32_t i = 0; i < mCacheLines.size(); i ++) { 825 delete mCacheLines[i]; 826 } 827 mCacheLines.clear(); 828 829 mDefault.clear(); 830#ifndef ANDROID_RS_SERIALIZE 831 if (mLibrary) { 832 FT_Done_FreeType( mLibrary ); 833 mLibrary = NULL; 834 } 835#endif //ANDROID_RS_SERIALIZE 836} 837 838#ifndef ANDROID_RS_SERIALIZE 839bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { 840 if ((uint32_t)bitmap->rows > mMaxHeight) { 841 return false; 842 } 843 844 if (mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) { 845 *retOriginX = mCurrentCol; 846 *retOriginY = mCurrentRow; 847 mCurrentCol += bitmap->width; 848 mDirty = true; 849 return true; 850 } 851 852 return false; 853} 854#endif //ANDROID_RS_SERIALIZE 855 856namespace android { 857namespace renderscript { 858 859RsFont rsi_FontCreateFromFile(Context *rsc, 860 char const *name, size_t name_length, 861 float fontSize, uint32_t dpi) { 862 Font *newFont = Font::create(rsc, name, fontSize, dpi); 863 if (newFont) { 864 newFont->incUserRef(); 865 } 866 return newFont; 867} 868 869RsFont rsi_FontCreateFromMemory(Context *rsc, 870 char const *name, size_t name_length, 871 float fontSize, uint32_t dpi, 872 const void *data, size_t data_length) { 873 Font *newFont = Font::create(rsc, name, fontSize, dpi, data, data_length); 874 if (newFont) { 875 newFont->incUserRef(); 876 } 877 return newFont; 878} 879 880} // renderscript 881} // android 882