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