rsFont.cpp revision d3e0ad43dc758c409fc23d1893dab67b18520c24
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#ifndef ANDROID_RS_BUILD_FOR_HOST 19#include "rsContext.h" 20#else 21#include "rsContextHostStub.h" 22#endif 23 24#include "rsFont.h" 25#include "rsProgramFragment.h" 26#include FT_BITMAP_H 27 28#include <GLES/gl.h> 29#include <GLES/glext.h> 30#include <GLES2/gl2.h> 31#include <GLES2/gl2ext.h> 32 33using namespace android; 34using namespace android::renderscript; 35 36Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) 37{ 38 mInitialized = false; 39 mHasKerning = false; 40} 41 42bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi) 43{ 44 if(mInitialized) { 45 LOGE("Reinitialization of fonts not supported"); 46 return false; 47 } 48 49 String8 fontsDir("/fonts/"); 50 String8 fullPath(getenv("ANDROID_ROOT")); 51 fullPath += fontsDir; 52 fullPath += name; 53 54 FT_Error error = FT_New_Face(mRSC->mStateFont.mLibrary, fullPath.string(), 0, &mFace); 55 if(error) { 56 LOGE("Unable to initialize font %s", fullPath.string()); 57 return false; 58 } 59 60 mFontName = name; 61 mFontSize = fontSize; 62 mDpi = dpi; 63 64 //LOGE("Font initialized: %s", fullPath.string()); 65 66 error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0); 67 if(error) { 68 LOGE("Unable to set font size on %s", fullPath.string()); 69 return false; 70 } 71 72 mHasKerning = FT_HAS_KERNING(mFace); 73 LOGE("Kerning: %i", mHasKerning); 74 75 mInitialized = true; 76 return true; 77} 78 79void Font::invalidateTextureCache() 80{ 81 for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { 82 mCachedGlyphs.valueAt(i)->mIsValid = false; 83 } 84} 85 86void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y) 87{ 88 FontState *state = &mRSC->mStateFont; 89 90 int nPenX = x + glyph->mBitmapLeft; 91 int nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight; 92 93 state->appendMeshQuad(nPenX, nPenY, 0, 94 glyph->mBitmapMinU, glyph->mBitmapMaxV, 95 96 nPenX + (int)glyph->mBitmapWidth, nPenY, 0, 97 glyph->mBitmapMaxU, glyph->mBitmapMaxV, 98 99 nPenX + (int)glyph->mBitmapWidth, nPenY - (int)glyph->mBitmapHeight, 0, 100 glyph->mBitmapMaxU, glyph->mBitmapMinV, 101 102 nPenX, nPenY - (int)glyph->mBitmapHeight, 0, 103 glyph->mBitmapMinU, glyph->mBitmapMinV); 104} 105 106void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y) 107{ 108 if(!mInitialized || numGlyphs == 0 || text == NULL || len == 0) { 109 return; 110 } 111 112 int penX = x, penY = y; 113 int glyphsLeft = 1; 114 if(numGlyphs > 0) { 115 glyphsLeft = numGlyphs; 116 } 117 118 size_t index = start; 119 size_t nextIndex = 0; 120 121 while (glyphsLeft > 0) { 122 123 int32_t utfChar = utf32_at(text, len, index, &nextIndex); 124 125 // Reached the end of the string or encountered 126 if(utfChar < 0) { 127 break; 128 } 129 130 // Move to the next character in the array 131 index = nextIndex; 132 133 CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar); 134 135 if(cachedGlyph == NULL) { 136 cachedGlyph = cacheGlyph((uint32_t)utfChar); 137 } 138 // Is the glyph still in texture cache? 139 if(!cachedGlyph->mIsValid) { 140 updateGlyphCache(cachedGlyph); 141 } 142 143 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage 144 if(cachedGlyph->mIsValid) { 145 drawCachedGlyph(cachedGlyph, penX, penY); 146 } 147 148 penX += (cachedGlyph->mAdvance.x >> 6); 149 150 // If we were given a specific number of glyphs, decrement 151 if(numGlyphs > 0) { 152 glyphsLeft --; 153 } 154 } 155} 156 157void Font::updateGlyphCache(CachedGlyphInfo *glyph) 158{ 159 if(!glyph->mBitmapValid) { 160 161 FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER ); 162 if(error) { 163 LOGE("Couldn't load glyph."); 164 return; 165 } 166 167 glyph->mAdvance = mFace->glyph->advance; 168 glyph->mBitmapLeft = mFace->glyph->bitmap_left; 169 glyph->mBitmapTop = mFace->glyph->bitmap_top; 170 171 FT_Bitmap *bitmap = &mFace->glyph->bitmap; 172 173 FT_Bitmap_New(&glyph->mBitmap); 174 FT_Bitmap_Copy(mRSC->mStateFont.mLibrary, bitmap, &glyph->mBitmap); 175 176 glyph->mBitmapValid = true; 177 } 178 179 // Now copy the bitmap into the cache texture 180 uint32_t startX = 0; 181 uint32_t startY = 0; 182 183 // Let the font state figure out where to put the bitmap 184 FontState *state = &mRSC->mStateFont; 185 glyph->mIsValid = state->cacheBitmap(&glyph->mBitmap, &startX, &startY); 186 187 if(!glyph->mIsValid) { 188 return; 189 } 190 191 uint32_t endX = startX + glyph->mBitmap.width; 192 uint32_t endY = startY + glyph->mBitmap.rows; 193 194 glyph->mBitmapMinX = startX; 195 glyph->mBitmapMinY = startY; 196 glyph->mBitmapWidth = glyph->mBitmap.width; 197 glyph->mBitmapHeight = glyph->mBitmap.rows; 198 199 uint32_t cacheWidth = state->getCacheTextureType()->getDimX(); 200 uint32_t cacheHeight = state->getCacheTextureType()->getDimY(); 201 202 glyph->mBitmapMinU = (float)startX / (float)cacheWidth; 203 glyph->mBitmapMinV = (float)startY / (float)cacheHeight; 204 glyph->mBitmapMaxU = (float)endX / (float)cacheWidth; 205 glyph->mBitmapMaxV = (float)endY / (float)cacheHeight; 206} 207 208Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) 209{ 210 CachedGlyphInfo *newGlyph = new CachedGlyphInfo(); 211 mCachedGlyphs.add(glyph, newGlyph); 212 213 newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph); 214 newGlyph->mIsValid = false; 215 newGlyph->mBitmapValid = false; 216 217 //LOGE("Glyph = %c, face index: %u", (unsigned char)glyph, newGlyph->mGlyphIndex); 218 219 updateGlyphCache(newGlyph); 220 221 return newGlyph; 222} 223 224Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi) 225{ 226 Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts; 227 228 for(uint32_t i = 0; i < activeFonts.size(); i ++) { 229 Font *ithFont = activeFonts[i]; 230 if(ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) { 231 ithFont->incUserRef(); 232 return ithFont; 233 } 234 } 235 236 Font *newFont = new Font(rsc); 237 bool isInitialized = newFont->init(name, fontSize, dpi); 238 if(isInitialized) { 239 newFont->incUserRef(); 240 activeFonts.push(newFont); 241 return newFont; 242 } 243 244 delete newFont; 245 return NULL; 246 247} 248 249Font::~Font() 250{ 251 if(mFace) { 252 FT_Done_Face(mFace); 253 } 254 255 for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) { 256 if (mRSC->mStateFont.mActiveFonts[ct] == this) { 257 mRSC->mStateFont.mActiveFonts.removeAt(ct); 258 break; 259 } 260 } 261 262 for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { 263 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i); 264 if(glyph->mBitmapValid) { 265 FT_Bitmap_Done(mRSC->mStateFont.mLibrary, &glyph->mBitmap); 266 } 267 delete glyph; 268 } 269} 270 271FontState::FontState() 272{ 273 mInitialized = false; 274 mMaxNumberOfQuads = 1024; 275 mCurrentQuadIndex = 0; 276 mRSC = NULL; 277} 278 279FontState::~FontState() 280{ 281 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 282 delete mCacheLines[i]; 283 } 284 285 rsAssert(!mActiveFonts.size()); 286} 287 288void FontState::init(Context *rsc) 289{ 290 FT_Error error; 291 292 if(!mLibrary) { 293 error = FT_Init_FreeType(&mLibrary); 294 if(error) { 295 LOGE("Unable to initialize freetype"); 296 return; 297 } 298 } 299 300 mRSC = rsc; 301 302 mDefault.set(Font::create(rsc, "DroidSans.ttf", 16, 96)); 303} 304 305void FontState::flushAllAndInvalidate() 306{ 307 if(mCurrentQuadIndex != 0) { 308 issueDrawCommand(); 309 mCurrentQuadIndex = 0; 310 } 311 for(uint32_t i = 0; i < mActiveFonts.size(); i ++) { 312 mActiveFonts[i]->invalidateTextureCache(); 313 } 314 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 315 mCacheLines[i]->mCurrentCol = 0; 316 } 317} 318 319bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) 320{ 321 // If the glyph is too tall, don't cache it 322 if((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) { 323 LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); 324 return false; 325 } 326 327 // Now copy the bitmap into the cache texture 328 uint32_t startX = 0; 329 uint32_t startY = 0; 330 331 bool bitmapFit = false; 332 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 333 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); 334 if(bitmapFit) { 335 break; 336 } 337 } 338 339 // If the new glyph didn't fit, flush the state so far and invalidate everything 340 if(!bitmapFit) { 341 flushAllAndInvalidate(); 342 343 // Try to fit it again 344 for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 345 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); 346 if(bitmapFit) { 347 break; 348 } 349 } 350 351 // if we still don't fit, something is wrong and we shouldn't draw 352 if(!bitmapFit) { 353 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); 354 return false; 355 } 356 } 357 358 *retOriginX = startX; 359 *retOriginY = startY; 360 361 uint32_t endX = startX + bitmap->width; 362 uint32_t endY = startY + bitmap->rows; 363 364 //LOGE("Bitmap width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); 365 366 uint32_t cacheWidth = getCacheTextureType()->getDimX(); 367 368 unsigned char *cacheBuffer = (unsigned char*)mTextTexture->getPtr(); 369 unsigned char *bitmapBuffer = bitmap->buffer; 370 371 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 372 for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) { 373 for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) { 374 unsigned char tempCol = bitmapBuffer[bY * bitmap->width + bX]; 375 cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol; 376 } 377 } 378 379 // This will dirty the texture and the shader so next time 380 // we draw it will upload the data 381 mTextTexture->deferedUploadToTexture(mRSC, false, 0); 382 mFontShaderF->bindTexture(0, mTextTexture.get()); 383 384 // Some debug code 385 /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) { 386 LOGE("Cache Line: H: %u Empty Space: %f", 387 mCacheLines[i]->mMaxHeight, 388 (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f); 389 390 }*/ 391 392 return true; 393} 394 395void FontState::initRenderState() 396{ 397 uint32_t tmp[5] = { 398 RS_TEX_ENV_MODE_REPLACE, 1, 399 RS_TEX_ENV_MODE_NONE, 0, 400 0 401 }; 402 ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 5); 403 mFontShaderF.set(pf); 404 mFontShaderF->init(mRSC); 405 406 Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, 407 RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP); 408 mFontSampler.set(sampler); 409 mFontShaderF->bindSampler(0, sampler); 410 411 ProgramStore *fontStore = new ProgramStore(mRSC); 412 mFontProgramStore.set(fontStore); 413 mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS); 414 mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA); 415 mFontProgramStore->setDitherEnable(false); 416 mFontProgramStore->setDepthMask(false); 417} 418 419void FontState::initTextTexture() 420{ 421 const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1); 422 423 // We will allocate a texture to initially hold 32 character bitmaps 424 Type *texType = new Type(mRSC); 425 texType->setElement(alphaElem); 426 texType->setDimX(1024); 427 texType->setDimY(256); 428 texType->compute(); 429 430 Allocation *cacheAlloc = new Allocation(mRSC, texType); 431 mTextTexture.set(cacheAlloc); 432 mTextTexture->deferedUploadToTexture(mRSC, false, 0); 433 434 // Split up our cache texture into lines of certain widths 435 int nextLine = 0; 436 mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0)); 437 nextLine += mCacheLines.top()->mMaxHeight; 438 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); 439 nextLine += mCacheLines.top()->mMaxHeight; 440 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); 441 nextLine += mCacheLines.top()->mMaxHeight; 442 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); 443 nextLine += mCacheLines.top()->mMaxHeight; 444 mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0)); 445 nextLine += mCacheLines.top()->mMaxHeight; 446 mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0)); 447} 448 449// Avoid having to reallocate memory and render quad by quad 450void FontState::initVertexArrayBuffers() 451{ 452 // Now lets write index data 453 const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); 454 Type *indexType = new Type(mRSC); 455 uint32_t numIndicies = mMaxNumberOfQuads * 6; 456 indexType->setDimX(numIndicies); 457 indexType->setElement(indexElem); 458 indexType->compute(); 459 460 Allocation *indexAlloc = new Allocation(mRSC, indexType); 461 uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr(); 462 463 // Four verts, two triangles , six indices per quad 464 for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) { 465 int i6 = i * 6; 466 int i4 = i * 4; 467 468 indexPtr[i6 + 0] = i4 + 0; 469 indexPtr[i6 + 1] = i4 + 1; 470 indexPtr[i6 + 2] = i4 + 2; 471 472 indexPtr[i6 + 3] = i4 + 0; 473 indexPtr[i6 + 4] = i4 + 2; 474 indexPtr[i6 + 5] = i4 + 3; 475 } 476 477 indexAlloc->deferedUploadToBufferObject(mRSC); 478 mIndexBuffer.set(indexAlloc); 479 480 const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3); 481 const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2); 482 483 const Element *elemArray[2]; 484 elemArray[0] = posElem; 485 elemArray[1] = texElem; 486 487 String8 posName("position"); 488 String8 texName("texture0"); 489 490 const char *nameArray[2]; 491 nameArray[0] = posName.string(); 492 nameArray[1] = texName.string(); 493 size_t lengths[2]; 494 lengths[0] = posName.size(); 495 lengths[1] = texName.size(); 496 497 const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths); 498 499 Type *vertexDataType = new Type(mRSC); 500 vertexDataType->setDimX(mMaxNumberOfQuads * 4); 501 vertexDataType->setElement(vertexDataElem); 502 vertexDataType->compute(); 503 504 Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType); 505 mTextMeshPtr = (float*)vertexAlloc->getPtr(); 506 507 mVertexArray.set(vertexAlloc); 508} 509 510// We don't want to allocate anything unless we actually draw text 511void FontState::checkInit() 512{ 513 if(mInitialized) { 514 return; 515 } 516 517 initTextTexture(); 518 initRenderState(); 519 520 initVertexArrayBuffers(); 521 522 /*mTextMeshRefs = new ObjectBaseRef<SimpleMesh>[mNumMeshes]; 523 524 for(uint32_t i = 0; i < mNumMeshes; i ++){ 525 SimpleMesh *textMesh = createTextMesh(); 526 mTextMeshRefs[i].set(textMesh); 527 }*/ 528 529 mInitialized = true; 530} 531 532void FontState::issueDrawCommand() { 533 534 ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex()); 535 mRSC->setVertex(mRSC->getDefaultProgramVertex()); 536 537 ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment()); 538 mRSC->setFragment(mFontShaderF.get()); 539 540 ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore()); 541 mRSC->setFragmentStore(mFontProgramStore.get()); 542 543 if (!mRSC->setupCheck()) { 544 mRSC->setVertex((ProgramVertex *)tmpV.get()); 545 mRSC->setFragment((ProgramFragment *)tmpF.get()); 546 mRSC->setFragmentStore((ProgramStore *)tmpPS.get()); 547 return; 548 } 549 550 float *vtx = (float*)mVertexArray->getPtr(); 551 float *tex = vtx + 3; 552 553 VertexArray va; 554 va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "position"); 555 va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "texture0"); 556 va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache); 557 558 mIndexBuffer->uploadCheck(mRSC); 559 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID()); 560 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0)); 561 562 // Reset the state 563 mRSC->setVertex((ProgramVertex *)tmpV.get()); 564 mRSC->setFragment((ProgramFragment *)tmpF.get()); 565 mRSC->setFragmentStore((ProgramStore *)tmpPS.get()); 566} 567 568void FontState::appendMeshQuad(float x1, float y1, float z1, 569 float u1, float v1, 570 float x2, float y2, float z2, 571 float u2, float v2, 572 float x3, float y3, float z3, 573 float u3, float v3, 574 float x4, float y4, float z4, 575 float u4, float v4) 576{ 577 const uint32_t vertsPerQuad = 4; 578 const uint32_t floatsPerVert = 5; 579 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 580 581 // Cull things that are off the screen 582 float width = (float)mRSC->getWidth(); 583 float height = (float)mRSC->getHeight(); 584 585 if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { 586 return; 587 } 588 589 /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1); 590 LOGE("V1 x: %f y: %f z: %f", x2, y2, z2); 591 LOGE("V2 x: %f y: %f z: %f", x3, y3, z3); 592 LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/ 593 594 (*currentPos++) = x1; 595 (*currentPos++) = y1; 596 (*currentPos++) = z1; 597 (*currentPos++) = u1; 598 (*currentPos++) = v1; 599 600 (*currentPos++) = x2; 601 (*currentPos++) = y2; 602 (*currentPos++) = z2; 603 (*currentPos++) = u2; 604 (*currentPos++) = v2; 605 606 (*currentPos++) = x3; 607 (*currentPos++) = y3; 608 (*currentPos++) = z3; 609 (*currentPos++) = u3; 610 (*currentPos++) = v3; 611 612 (*currentPos++) = x4; 613 (*currentPos++) = y4; 614 (*currentPos++) = z4; 615 (*currentPos++) = u4; 616 (*currentPos++) = v4; 617 618 mCurrentQuadIndex ++; 619 620 if(mCurrentQuadIndex == mMaxNumberOfQuads) { 621 issueDrawCommand(); 622 mCurrentQuadIndex = 0; 623 } 624} 625 626void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y) 627{ 628 checkInit(); 629 630 String8 text8(text); 631 632 // Render code here 633 Font *currentFont = mRSC->getFont(); 634 currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y); 635 636 if(mCurrentQuadIndex != 0) { 637 issueDrawCommand(); 638 mCurrentQuadIndex = 0; 639 } 640} 641 642void FontState::renderText(const char *text, int x, int y) 643{ 644 size_t textLen = strlen(text); 645 renderText(text, textLen, 0, -1, x, y); 646} 647 648void FontState::renderText(Allocation *alloc, int x, int y) 649{ 650 if(!alloc) { 651 return; 652 } 653 654 const char *text = (const char *)alloc->getPtr(); 655 size_t allocSize = alloc->getType()->getSizeBytes(); 656 renderText(text, allocSize, 0, -1, x, y); 657} 658 659void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y) 660{ 661 if(!alloc) { 662 return; 663 } 664 665 const char *text = (const char *)alloc->getPtr(); 666 size_t allocSize = alloc->getType()->getSizeBytes(); 667 renderText(text, allocSize, start, len, x, y); 668} 669 670void FontState::deinit(Context *rsc) 671{ 672 if(mLibrary) { 673 FT_Done_FreeType( mLibrary ); 674 } 675 676 delete mDefault.get(); 677 mDefault.clear(); 678} 679 680namespace android { 681namespace renderscript { 682 683RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi) 684{ 685 return Font::create(rsc, name, fontSize, dpi); 686} 687 688} // renderscript 689} // android 690