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