FontRenderer.cpp revision f2d8ccc15d7272b3416f73605c1f31d1d346bd40
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "OpenGLRenderer" 18 19#include <SkUtils.h> 20 21#include <cutils/properties.h> 22 23#include <utils/Log.h> 24 25#include "RenderScript.h" 26 27#include "utils/Timing.h" 28#include "Caches.h" 29#include "Debug.h" 30#include "FontRenderer.h" 31#include "Rect.h" 32 33namespace android { 34namespace uirenderer { 35 36// blur inputs smaller than this constant will bypass renderscript 37#define RS_MIN_INPUT_CUTOFF 10000 38 39/////////////////////////////////////////////////////////////////////////////// 40// FontRenderer 41/////////////////////////////////////////////////////////////////////////////// 42 43static bool sLogFontRendererCreate = true; 44 45FontRenderer::FontRenderer() : 46 mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) { 47 48 if (sLogFontRendererCreate) { 49 INIT_LOGD("Creating FontRenderer"); 50 } 51 52 mGammaTable = NULL; 53 mInitialized = false; 54 mMaxNumberOfQuads = 1024; 55 mCurrentQuadIndex = 0; 56 mLastQuadIndex = 0; 57 58 mTextMesh = NULL; 59 mCurrentCacheTexture = NULL; 60 61 mLinearFiltering = false; 62 63 mIndexBufferID = 0; 64 65 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH; 66 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT; 67 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH; 68 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT; 69 70 char property[PROPERTY_VALUE_MAX]; 71 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) { 72 mSmallCacheWidth = atoi(property); 73 } 74 75 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) { 76 mSmallCacheHeight = atoi(property); 77 } 78 79 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) { 80 mLargeCacheWidth = atoi(property); 81 } 82 83 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) { 84 mLargeCacheHeight = atoi(property); 85 } 86 87 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; 88 mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth; 89 mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight; 90 mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth; 91 mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight; 92 93 if (sLogFontRendererCreate) { 94 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", 95 mSmallCacheWidth, mSmallCacheHeight, 96 mLargeCacheWidth, mLargeCacheHeight >> 1, 97 mLargeCacheWidth, mLargeCacheHeight >> 1, 98 mLargeCacheWidth, mLargeCacheHeight); 99 } 100 101 sLogFontRendererCreate = false; 102} 103 104FontRenderer::~FontRenderer() { 105 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 106 delete mCacheTextures[i]; 107 } 108 mCacheTextures.clear(); 109 110 if (mInitialized) { 111 // Unbinding the buffer shouldn't be necessary but it crashes with some drivers 112 Caches::getInstance().unbindIndicesBuffer(); 113 glDeleteBuffers(1, &mIndexBufferID); 114 115 delete[] mTextMesh; 116 } 117 118 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 119 while (it.next()) { 120 delete it.value(); 121 } 122 mActiveFonts.clear(); 123} 124 125void FontRenderer::flushAllAndInvalidate() { 126 if (mCurrentQuadIndex != 0) { 127 issueDrawCommand(); 128 } 129 130 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 131 while (it.next()) { 132 it.value()->invalidateTextureCache(); 133 } 134 135 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 136 mCacheTextures[i]->init(); 137 } 138 139#if DEBUG_FONT_RENDERER 140 uint16_t totalGlyphs = 0; 141 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 142 totalGlyphs += mCacheTextures[i]->getGlyphCount(); 143 // Erase caches, just as a debugging facility 144 if (mCacheTextures[i]->getTexture()) { 145 memset(mCacheTextures[i]->getTexture(), 0, 146 mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight()); 147 } 148 } 149 ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs); 150#endif 151} 152 153void FontRenderer::flushLargeCaches() { 154 // Start from 1; don't deallocate smallest/default texture 155 for (uint32_t i = 1; i < mCacheTextures.size(); i++) { 156 CacheTexture* cacheTexture = mCacheTextures[i]; 157 if (cacheTexture->getTexture()) { 158 cacheTexture->init(); 159 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 160 while (it.next()) { 161 it.value()->invalidateTextureCache(cacheTexture); 162 } 163 cacheTexture->releaseTexture(); 164 } 165 } 166} 167 168CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph, 169 uint32_t* startX, uint32_t* startY) { 170 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 171 if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) { 172 return mCacheTextures[i]; 173 } 174 } 175 // Could not fit glyph into current cache textures 176 return NULL; 177} 178 179void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 180 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { 181 checkInit(); 182 cachedGlyph->mIsValid = false; 183 // If the glyph is too tall, don't cache it 184 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > 185 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) { 186 ALOGE("Font size too large to fit in cache. width, height = %i, %i", 187 (int) glyph.fWidth, (int) glyph.fHeight); 188 return; 189 } 190 191 // Now copy the bitmap into the cache texture 192 uint32_t startX = 0; 193 uint32_t startY = 0; 194 195 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 196 197 if (!cacheTexture) { 198 if (!precaching) { 199 // If the new glyph didn't fit and we are not just trying to precache it, 200 // clear out the cache and try again 201 flushAllAndInvalidate(); 202 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 203 } 204 205 if (!cacheTexture) { 206 // either the glyph didn't fit or we're precaching and will cache it when we draw 207 return; 208 } 209 } 210 211 cachedGlyph->mCacheTexture = cacheTexture; 212 213 *retOriginX = startX; 214 *retOriginY = startY; 215 216 uint32_t endX = startX + glyph.fWidth; 217 uint32_t endY = startY + glyph.fHeight; 218 219 uint32_t cacheWidth = cacheTexture->getWidth(); 220 221 if (!cacheTexture->getTexture()) { 222 Caches::getInstance().activeTexture(0); 223 // Large-glyph texture memory is allocated only as needed 224 cacheTexture->allocateTexture(); 225 } 226 227 // Tells us whether the glyphs is B&W (1 bit per pixel) 228 // or anti-aliased (8 bits per pixel) 229 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); 230 231 uint8_t* cacheBuffer = cacheTexture->getTexture(); 232 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 233 234 // Zero out the borders 235 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) { 236 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0; 237 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0; 238 } 239 240 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1; 241 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) { 242 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0; 243 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0; 244 } 245 246 // Copy the glyph image, taking the mask format into account 247 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 248 int stride = glyph.rowBytes(); 249 250 switch (format) { 251 case SkMask::kA8_Format: { 252 if (mGammaTable) { 253 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) { 254 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 255 uint8_t tempCol = bitmapBuffer[bY + bX]; 256 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 257 } 258 } 259 } else { 260 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) { 261 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY], 262 glyph.fWidth); 263 } 264 } 265 break; 266 } 267 case SkMask::kBW_Format: { 268 static const uint8_t COLORS[2] = { 0, 255 }; 269 270 for (cacheY = startY; cacheY < endY; cacheY++) { 271 cacheX = startX; 272 int rowBytes = stride; 273 uint8_t* buffer = bitmapBuffer; 274 275 while (--rowBytes >= 0) { 276 uint8_t b = *buffer++; 277 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) { 278 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1]; 279 } 280 } 281 282 bitmapBuffer += stride; 283 } 284 break; 285 } 286 default: 287 ALOGW("Unkown glyph format: 0x%x", format); 288 break; 289 } 290 291 cachedGlyph->mIsValid = true; 292} 293 294CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 295 CacheTexture* cacheTexture = new CacheTexture(width, height); 296 297 if (allocate) { 298 Caches::getInstance().activeTexture(0); 299 cacheTexture->allocateTexture(); 300 } 301 302 return cacheTexture; 303} 304 305void FontRenderer::initTextTexture() { 306 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 307 delete mCacheTextures[i]; 308 } 309 mCacheTextures.clear(); 310 311 mUploadTexture = false; 312 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true)); 313 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false)); 314 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false)); 315 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false)); 316 mCurrentCacheTexture = mCacheTextures[0]; 317} 318 319// Avoid having to reallocate memory and render quad by quad 320void FontRenderer::initVertexArrayBuffers() { 321 uint32_t numIndices = mMaxNumberOfQuads * 6; 322 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 323 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 324 325 // Four verts, two triangles , six indices per quad 326 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 327 int i6 = i * 6; 328 int i4 = i * 4; 329 330 indexBufferData[i6 + 0] = i4 + 0; 331 indexBufferData[i6 + 1] = i4 + 1; 332 indexBufferData[i6 + 2] = i4 + 2; 333 334 indexBufferData[i6 + 3] = i4 + 0; 335 indexBufferData[i6 + 4] = i4 + 2; 336 indexBufferData[i6 + 5] = i4 + 3; 337 } 338 339 glGenBuffers(1, &mIndexBufferID); 340 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 341 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 342 343 free(indexBufferData); 344 345 uint32_t coordSize = 2; 346 uint32_t uvSize = 2; 347 uint32_t vertsPerQuad = 4; 348 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 349 mTextMesh = new float[vertexBufferSize]; 350} 351 352// We don't want to allocate anything unless we actually draw text 353void FontRenderer::checkInit() { 354 if (mInitialized) { 355 return; 356 } 357 358 initTextTexture(); 359 initVertexArrayBuffers(); 360 361 mInitialized = true; 362} 363 364void FontRenderer::updateDrawParams() { 365 if (mCurrentQuadIndex != mLastQuadIndex) { 366 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6)); 367 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex); 368 mDrawCacheTextures.add(mCurrentCacheTexture); 369 mLastQuadIndex = mCurrentQuadIndex; 370 } 371} 372 373void FontRenderer::checkTextureUpdate() { 374 if (!mUploadTexture) { 375 return; 376 } 377 378 Caches& caches = Caches::getInstance(); 379 GLuint lastTextureId = 0; 380 // Iterate over all the cache textures and see which ones need to be updated 381 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 382 CacheTexture* cacheTexture = mCacheTextures[i]; 383 if (cacheTexture->isDirty() && cacheTexture->getTexture()) { 384 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer 385 // of data. So expand the dirty rect to the encompassing horizontal stripe. 386 const Rect* dirtyRect = cacheTexture->getDirtyRect(); 387 uint32_t x = 0; 388 uint32_t y = dirtyRect->top; 389 uint32_t width = cacheTexture->getWidth(); 390 uint32_t height = dirtyRect->getHeight(); 391 void* textureData = cacheTexture->getTexture() + y * width; 392 393 if (cacheTexture->getTextureId() != lastTextureId) { 394 lastTextureId = cacheTexture->getTextureId(); 395 caches.activeTexture(0); 396 glBindTexture(GL_TEXTURE_2D, lastTextureId); 397 } 398#if DEBUG_FONT_RENDERER 399 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d", 400 i, x, y, width, height); 401#endif 402 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, 403 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 404 cacheTexture->setDirty(false); 405 } 406 } 407 408 mUploadTexture = false; 409} 410 411void FontRenderer::issueDrawCommand() { 412 updateDrawParams(); 413 checkTextureUpdate(); 414 415 Caches& caches = Caches::getInstance(); 416 caches.bindIndicesBuffer(mIndexBufferID); 417 if (!mDrawn) { 418 float* buffer = mTextMesh; 419 int offset = 2; 420 421 bool force = caches.unbindMeshBuffer(); 422 caches.bindPositionVertexPointer(force, buffer); 423 caches.bindTexCoordsVertexPointer(force, buffer + offset); 424 } 425 426 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) { 427 uint16_t* offset = mDrawOffsets[i]; 428 uint32_t count = mDrawCounts[i]; 429 CacheTexture* texture = mDrawCacheTextures[i]; 430 431 caches.activeTexture(0); 432 glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); 433 434 texture->setLinearFiltering(mLinearFiltering, false); 435 436 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset); 437 } 438 439 mDrawn = true; 440 441 mCurrentQuadIndex = 0; 442 mLastQuadIndex = 0; 443 mDrawOffsets.clear(); 444 mDrawCounts.clear(); 445 mDrawCacheTextures.clear(); 446} 447 448void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 449 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 450 float x4, float y4, float u4, float v4, CacheTexture* texture) { 451 if (texture != mCurrentCacheTexture) { 452 updateDrawParams(); 453 // Now use the new texture id 454 mCurrentCacheTexture = texture; 455 } 456 457 const uint32_t vertsPerQuad = 4; 458 const uint32_t floatsPerVert = 4; 459 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 460 461 (*currentPos++) = x1; 462 (*currentPos++) = y1; 463 (*currentPos++) = u1; 464 (*currentPos++) = v1; 465 466 (*currentPos++) = x2; 467 (*currentPos++) = y2; 468 (*currentPos++) = u2; 469 (*currentPos++) = v2; 470 471 (*currentPos++) = x3; 472 (*currentPos++) = y3; 473 (*currentPos++) = u3; 474 (*currentPos++) = v3; 475 476 (*currentPos++) = x4; 477 (*currentPos++) = y4; 478 (*currentPos++) = u4; 479 (*currentPos++) = v4; 480 481 mCurrentQuadIndex++; 482} 483 484void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 485 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 486 float x4, float y4, float u4, float v4, CacheTexture* texture) { 487 488 if (mClip && 489 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 490 return; 491 } 492 493 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 494 495 if (mBounds) { 496 mBounds->left = fmin(mBounds->left, x1); 497 mBounds->top = fmin(mBounds->top, y3); 498 mBounds->right = fmax(mBounds->right, x3); 499 mBounds->bottom = fmax(mBounds->bottom, y1); 500 } 501 502 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 503 issueDrawCommand(); 504 } 505} 506 507void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 508 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 509 float x4, float y4, float u4, float v4, CacheTexture* texture) { 510 511 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 512 513 if (mBounds) { 514 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 515 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 516 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 517 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 518 } 519 520 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 521 issueDrawCommand(); 522 } 523} 524 525void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) { 526 mCurrentFont = Font::create(this, paint, matrix); 527} 528 529FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 530 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 531 checkInit(); 532 533 if (!mCurrentFont) { 534 DropShadow image; 535 image.width = 0; 536 image.height = 0; 537 image.image = NULL; 538 image.penX = 0; 539 image.penY = 0; 540 return image; 541 } 542 543 mDrawn = false; 544 mClip = NULL; 545 mBounds = NULL; 546 547 Rect bounds; 548 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 549 550 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 551 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 552 553 // Align buffers for renderscript usage 554 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 555 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 556 } 557 558 int size = paddedWidth * paddedHeight; 559 uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 560 memset(dataBuffer, 0, size); 561 562 int penX = radius - bounds.left; 563 int penY = radius - bounds.bottom; 564 565 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 566 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 567 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 568 569 DropShadow image; 570 image.width = paddedWidth; 571 image.height = paddedHeight; 572 image.image = dataBuffer; 573 image.penX = penX; 574 image.penY = penY; 575 576 return image; 577} 578 579void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 580 checkInit(); 581 582 mDrawn = false; 583 mBounds = bounds; 584 mClip = clip; 585} 586 587void FontRenderer::finishRender() { 588 mBounds = NULL; 589 mClip = NULL; 590 591 if (mCurrentQuadIndex != 0) { 592 issueDrawCommand(); 593 } 594} 595 596void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) { 597 Font* font = Font::create(this, paint, matrix); 598 font->precache(paint, text, numGlyphs); 599} 600 601bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 602 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 603 const float* positions, Rect* bounds) { 604 if (!mCurrentFont) { 605 ALOGE("No font set"); 606 return false; 607 } 608 609 initRender(clip, bounds); 610 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 611 finishRender(); 612 613 return mDrawn; 614} 615 616bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 617 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 618 float hOffset, float vOffset, Rect* bounds) { 619 if (!mCurrentFont) { 620 ALOGE("No font set"); 621 return false; 622 } 623 624 initRender(clip, bounds); 625 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 626 finishRender(); 627 628 return mDrawn; 629} 630 631void FontRenderer::removeFont(const Font* font) { 632 mActiveFonts.remove(font->getDescription()); 633 634 if (mCurrentFont == font) { 635 mCurrentFont = NULL; 636 } 637} 638 639void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 640 // Compute gaussian weights for the blur 641 // e is the euler's number 642 float e = 2.718281828459045f; 643 float pi = 3.1415926535897932f; 644 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 645 // x is of the form [-radius .. 0 .. radius] 646 // and sigma varies with radius. 647 // Based on some experimental radius values and sigma's 648 // we approximately fit sigma = f(radius) as 649 // sigma = radius * 0.3 + 0.6 650 // The larger the radius gets, the more our gaussian blur 651 // will resemble a box blur since with large sigma 652 // the gaussian curve begins to lose its shape 653 float sigma = 0.3f * (float) radius + 0.6f; 654 655 // Now compute the coefficints 656 // We will store some redundant values to save some math during 657 // the blur calculations 658 // precompute some values 659 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 660 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 661 662 float normalizeFactor = 0.0f; 663 for (int32_t r = -radius; r <= radius; r ++) { 664 float floatR = (float) r; 665 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 666 normalizeFactor += weights[r + radius]; 667 } 668 669 //Now we need to normalize the weights because all our coefficients need to add up to one 670 normalizeFactor = 1.0f / normalizeFactor; 671 for (int32_t r = -radius; r <= radius; r ++) { 672 weights[r + radius] *= normalizeFactor; 673 } 674} 675 676void FontRenderer::horizontalBlur(float* weights, int32_t radius, 677 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 678 float blurredPixel = 0.0f; 679 float currentPixel = 0.0f; 680 681 for (int32_t y = 0; y < height; y ++) { 682 683 const uint8_t* input = source + y * width; 684 uint8_t* output = dest + y * width; 685 686 for (int32_t x = 0; x < width; x ++) { 687 blurredPixel = 0.0f; 688 const float* gPtr = weights; 689 // Optimization for non-border pixels 690 if (x > radius && x < (width - radius)) { 691 const uint8_t *i = input + (x - radius); 692 for (int r = -radius; r <= radius; r ++) { 693 currentPixel = (float) (*i); 694 blurredPixel += currentPixel * gPtr[0]; 695 gPtr++; 696 i++; 697 } 698 } else { 699 for (int32_t r = -radius; r <= radius; r ++) { 700 // Stepping left and right away from the pixel 701 int validW = x + r; 702 if (validW < 0) { 703 validW = 0; 704 } 705 if (validW > width - 1) { 706 validW = width - 1; 707 } 708 709 currentPixel = (float) input[validW]; 710 blurredPixel += currentPixel * gPtr[0]; 711 gPtr++; 712 } 713 } 714 *output = (uint8_t)blurredPixel; 715 output ++; 716 } 717 } 718} 719 720void FontRenderer::verticalBlur(float* weights, int32_t radius, 721 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 722 float blurredPixel = 0.0f; 723 float currentPixel = 0.0f; 724 725 for (int32_t y = 0; y < height; y ++) { 726 uint8_t* output = dest + y * width; 727 728 for (int32_t x = 0; x < width; x ++) { 729 blurredPixel = 0.0f; 730 const float* gPtr = weights; 731 const uint8_t* input = source + x; 732 // Optimization for non-border pixels 733 if (y > radius && y < (height - radius)) { 734 const uint8_t *i = input + ((y - radius) * width); 735 for (int32_t r = -radius; r <= radius; r ++) { 736 currentPixel = (float)(*i); 737 blurredPixel += currentPixel * gPtr[0]; 738 gPtr++; 739 i += width; 740 } 741 } else { 742 for (int32_t r = -radius; r <= radius; r ++) { 743 int validH = y + r; 744 // Clamp to zero and width 745 if (validH < 0) { 746 validH = 0; 747 } 748 if (validH > height - 1) { 749 validH = height - 1; 750 } 751 752 const uint8_t *i = input + validH * width; 753 currentPixel = (float) (*i); 754 blurredPixel += currentPixel * gPtr[0]; 755 gPtr++; 756 } 757 } 758 *output = (uint8_t) blurredPixel; 759 output++; 760 } 761 } 762} 763 764void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { 765 if (width * height * radius < RS_MIN_INPUT_CUTOFF) { 766 float *gaussian = new float[2 * radius + 1]; 767 computeGaussianWeights(gaussian, radius); 768 769 uint8_t* scratch = new uint8_t[width * height]; 770 771 horizontalBlur(gaussian, radius, *image, scratch, width, height); 772 verticalBlur(gaussian, radius, scratch, *image, width, height); 773 774 delete[] gaussian; 775 delete[] scratch; 776 } 777 778 uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 779 780 if (mRs.get() == 0) { 781 mRs = new RSC::RS(); 782 if (!mRs->init(true, true)) { 783 ALOGE("blur RS failed to init"); 784 } 785 786 mRsElement = RSC::Element::A_8(mRs); 787 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement); 788 } 789 790 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 791 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, 792 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); 793 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, 794 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); 795 796 mRsScript->setRadius(radius); 797 mRsScript->blur(ain, aout); 798 799 // replace the original image's pointer, avoiding a copy back to the original buffer 800 delete *image; 801 *image = outImage; 802} 803 804}; // namespace uirenderer 805}; // namespace android 806