FontRenderer.cpp revision a4adcf0239039eb8f005be252409901c41b28839
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 184 // If the glyph bitmap is empty let's assum the glyph is valid 185 // so we can avoid doing extra work later on 186 if (glyph.fWidth == 0 || glyph.fHeight == 0) { 187 cachedGlyph->mIsValid = true; 188 cachedGlyph->mCacheTexture = NULL; 189 return; 190 } 191 192 cachedGlyph->mIsValid = false; 193 194 // If the glyph is too tall, don't cache it 195 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > 196 mCacheTextures[mCacheTextures.size() - 1]->getHeight()) { 197 ALOGE("Font size too large to fit in cache. width, height = %i, %i", 198 (int) glyph.fWidth, (int) glyph.fHeight); 199 return; 200 } 201 202 // Now copy the bitmap into the cache texture 203 uint32_t startX = 0; 204 uint32_t startY = 0; 205 206 CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 207 208 if (!cacheTexture) { 209 if (!precaching) { 210 // If the new glyph didn't fit and we are not just trying to precache it, 211 // clear out the cache and try again 212 flushAllAndInvalidate(); 213 cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); 214 } 215 216 if (!cacheTexture) { 217 // either the glyph didn't fit or we're precaching and will cache it when we draw 218 return; 219 } 220 } 221 222 cachedGlyph->mCacheTexture = cacheTexture; 223 224 *retOriginX = startX; 225 *retOriginY = startY; 226 227 uint32_t endX = startX + glyph.fWidth; 228 uint32_t endY = startY + glyph.fHeight; 229 230 uint32_t cacheWidth = cacheTexture->getWidth(); 231 232 if (!cacheTexture->getTexture()) { 233 Caches::getInstance().activeTexture(0); 234 // Large-glyph texture memory is allocated only as needed 235 cacheTexture->allocateTexture(); 236 } 237 238 // Tells us whether the glyphs is B&W (1 bit per pixel) 239 // or anti-aliased (8 bits per pixel) 240 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); 241 242 uint8_t* cacheBuffer = cacheTexture->getTexture(); 243 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 244 245 // Zero out the borders 246 for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) { 247 cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0; 248 cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0; 249 } 250 251 for (cacheY = startY - TEXTURE_BORDER_SIZE + 1; 252 cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) { 253 cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0; 254 cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0; 255 } 256 257 // Copy the glyph image, taking the mask format into account 258 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 259 int stride = glyph.rowBytes(); 260 261 switch (format) { 262 case SkMask::kA8_Format: { 263 if (mGammaTable) { 264 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) { 265 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 266 uint8_t tempCol = bitmapBuffer[bY + bX]; 267 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; 268 } 269 } 270 } else { 271 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) { 272 memcpy(&cacheBuffer[cacheY * cacheWidth + startX], &bitmapBuffer[bY], 273 glyph.fWidth); 274 } 275 } 276 break; 277 } 278 case SkMask::kBW_Format: { 279 static const uint8_t COLORS[2] = { 0, 255 }; 280 281 for (cacheY = startY; cacheY < endY; cacheY++) { 282 cacheX = startX; 283 int rowBytes = stride; 284 uint8_t* buffer = bitmapBuffer; 285 286 while (--rowBytes >= 0) { 287 uint8_t b = *buffer++; 288 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) { 289 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1]; 290 } 291 } 292 293 bitmapBuffer += stride; 294 } 295 break; 296 } 297 default: 298 ALOGW("Unkown glyph format: 0x%x", format); 299 break; 300 } 301 302 cachedGlyph->mIsValid = true; 303} 304 305CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { 306 CacheTexture* cacheTexture = new CacheTexture(width, height); 307 308 if (allocate) { 309 Caches::getInstance().activeTexture(0); 310 cacheTexture->allocateTexture(); 311 } 312 313 return cacheTexture; 314} 315 316void FontRenderer::initTextTexture() { 317 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 318 delete mCacheTextures[i]; 319 } 320 mCacheTextures.clear(); 321 322 mUploadTexture = false; 323 mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true)); 324 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false)); 325 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false)); 326 mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false)); 327 mCurrentCacheTexture = mCacheTextures[0]; 328} 329 330// Avoid having to reallocate memory and render quad by quad 331void FontRenderer::initVertexArrayBuffers() { 332 uint32_t numIndices = mMaxNumberOfQuads * 6; 333 uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); 334 uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); 335 336 // Four verts, two triangles , six indices per quad 337 for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { 338 int i6 = i * 6; 339 int i4 = i * 4; 340 341 indexBufferData[i6 + 0] = i4 + 0; 342 indexBufferData[i6 + 1] = i4 + 1; 343 indexBufferData[i6 + 2] = i4 + 2; 344 345 indexBufferData[i6 + 3] = i4 + 0; 346 indexBufferData[i6 + 4] = i4 + 2; 347 indexBufferData[i6 + 5] = i4 + 3; 348 } 349 350 glGenBuffers(1, &mIndexBufferID); 351 Caches::getInstance().bindIndicesBuffer(mIndexBufferID); 352 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); 353 354 free(indexBufferData); 355 356 uint32_t coordSize = 2; 357 uint32_t uvSize = 2; 358 uint32_t vertsPerQuad = 4; 359 uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; 360 mTextMesh = new float[vertexBufferSize]; 361} 362 363// We don't want to allocate anything unless we actually draw text 364void FontRenderer::checkInit() { 365 if (mInitialized) { 366 return; 367 } 368 369 initTextTexture(); 370 initVertexArrayBuffers(); 371 372 mInitialized = true; 373} 374 375void FontRenderer::updateDrawParams() { 376 if (mCurrentQuadIndex != mLastQuadIndex) { 377 mDrawOffsets.add((uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6)); 378 mDrawCounts.add(mCurrentQuadIndex - mLastQuadIndex); 379 mDrawCacheTextures.add(mCurrentCacheTexture); 380 mLastQuadIndex = mCurrentQuadIndex; 381 } 382} 383 384void FontRenderer::checkTextureUpdate() { 385 if (!mUploadTexture) { 386 return; 387 } 388 389 Caches& caches = Caches::getInstance(); 390 GLuint lastTextureId = 0; 391 // Iterate over all the cache textures and see which ones need to be updated 392 for (uint32_t i = 0; i < mCacheTextures.size(); i++) { 393 CacheTexture* cacheTexture = mCacheTextures[i]; 394 if (cacheTexture->isDirty() && cacheTexture->getTexture()) { 395 // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer 396 // of data. So expand the dirty rect to the encompassing horizontal stripe. 397 const Rect* dirtyRect = cacheTexture->getDirtyRect(); 398 uint32_t x = 0; 399 uint32_t y = dirtyRect->top; 400 uint32_t width = cacheTexture->getWidth(); 401 uint32_t height = dirtyRect->getHeight(); 402 void* textureData = cacheTexture->getTexture() + y * width; 403 404 if (cacheTexture->getTextureId() != lastTextureId) { 405 lastTextureId = cacheTexture->getTextureId(); 406 caches.activeTexture(0); 407 glBindTexture(GL_TEXTURE_2D, lastTextureId); 408 } 409#if DEBUG_FONT_RENDERER 410 ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d", 411 i, x, y, width, height); 412#endif 413 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, 414 GL_ALPHA, GL_UNSIGNED_BYTE, textureData); 415 cacheTexture->setDirty(false); 416 } 417 } 418 419 mUploadTexture = false; 420} 421 422void FontRenderer::issueDrawCommand() { 423 updateDrawParams(); 424 checkTextureUpdate(); 425 426 Caches& caches = Caches::getInstance(); 427 caches.bindIndicesBuffer(mIndexBufferID); 428 if (!mDrawn) { 429 float* buffer = mTextMesh; 430 int offset = 2; 431 432 bool force = caches.unbindMeshBuffer(); 433 caches.bindPositionVertexPointer(force, buffer); 434 caches.bindTexCoordsVertexPointer(force, buffer + offset); 435 } 436 437 for (uint32_t i = 0; i < mDrawOffsets.size(); i++) { 438 uint16_t* offset = mDrawOffsets[i]; 439 uint32_t count = mDrawCounts[i]; 440 CacheTexture* texture = mDrawCacheTextures[i]; 441 442 caches.activeTexture(0); 443 glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); 444 445 texture->setLinearFiltering(mLinearFiltering, false); 446 447 glDrawElements(GL_TRIANGLES, count * 6, GL_UNSIGNED_SHORT, offset); 448 } 449 450 mDrawn = true; 451 452 mCurrentQuadIndex = 0; 453 mLastQuadIndex = 0; 454 mDrawOffsets.clear(); 455 mDrawCounts.clear(); 456 mDrawCacheTextures.clear(); 457} 458 459void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 460 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 461 float x4, float y4, float u4, float v4, CacheTexture* texture) { 462 if (texture != mCurrentCacheTexture) { 463 updateDrawParams(); 464 // Now use the new texture id 465 mCurrentCacheTexture = texture; 466 } 467 468 const uint32_t vertsPerQuad = 4; 469 const uint32_t floatsPerVert = 4; 470 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 471 472 (*currentPos++) = x1; 473 (*currentPos++) = y1; 474 (*currentPos++) = u1; 475 (*currentPos++) = v1; 476 477 (*currentPos++) = x2; 478 (*currentPos++) = y2; 479 (*currentPos++) = u2; 480 (*currentPos++) = v2; 481 482 (*currentPos++) = x3; 483 (*currentPos++) = y3; 484 (*currentPos++) = u3; 485 (*currentPos++) = v3; 486 487 (*currentPos++) = x4; 488 (*currentPos++) = y4; 489 (*currentPos++) = u4; 490 (*currentPos++) = v4; 491 492 mCurrentQuadIndex++; 493} 494 495void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 496 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 497 float x4, float y4, float u4, float v4, CacheTexture* texture) { 498 499 if (mClip && 500 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 501 return; 502 } 503 504 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 505 506 if (mBounds) { 507 mBounds->left = fmin(mBounds->left, x1); 508 mBounds->top = fmin(mBounds->top, y3); 509 mBounds->right = fmax(mBounds->right, x3); 510 mBounds->bottom = fmax(mBounds->bottom, y1); 511 } 512 513 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 514 issueDrawCommand(); 515 } 516} 517 518void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 519 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 520 float x4, float y4, float u4, float v4, CacheTexture* texture) { 521 522 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 523 524 if (mBounds) { 525 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 526 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 527 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 528 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 529 } 530 531 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 532 issueDrawCommand(); 533 } 534} 535 536void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) { 537 mCurrentFont = Font::create(this, paint, matrix); 538} 539 540FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 541 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 542 checkInit(); 543 544 if (!mCurrentFont) { 545 DropShadow image; 546 image.width = 0; 547 image.height = 0; 548 image.image = NULL; 549 image.penX = 0; 550 image.penY = 0; 551 return image; 552 } 553 554 mDrawn = false; 555 mClip = NULL; 556 mBounds = NULL; 557 558 Rect bounds; 559 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 560 561 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 562 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 563 564 // Align buffers for renderscript usage 565 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 566 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 567 } 568 569 int size = paddedWidth * paddedHeight; 570 uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 571 memset(dataBuffer, 0, size); 572 573 int penX = radius - bounds.left; 574 int penY = radius - bounds.bottom; 575 576 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 577 // text has non-whitespace, so draw and blur to create the shadow 578 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 579 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 580 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 581 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 582 583 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 584 } 585 586 DropShadow image; 587 image.width = paddedWidth; 588 image.height = paddedHeight; 589 image.image = dataBuffer; 590 image.penX = penX; 591 image.penY = penY; 592 593 return image; 594} 595 596void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 597 checkInit(); 598 599 mDrawn = false; 600 mBounds = bounds; 601 mClip = clip; 602} 603 604void FontRenderer::finishRender() { 605 mBounds = NULL; 606 mClip = NULL; 607 608 if (mCurrentQuadIndex != 0) { 609 issueDrawCommand(); 610 } 611} 612 613void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) { 614 Font* font = Font::create(this, paint, matrix); 615 font->precache(paint, text, numGlyphs); 616} 617 618bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 619 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 620 const float* positions, Rect* bounds) { 621 if (!mCurrentFont) { 622 ALOGE("No font set"); 623 return false; 624 } 625 626 initRender(clip, bounds); 627 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 628 finishRender(); 629 630 return mDrawn; 631} 632 633bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 634 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 635 float hOffset, float vOffset, Rect* bounds) { 636 if (!mCurrentFont) { 637 ALOGE("No font set"); 638 return false; 639 } 640 641 initRender(clip, bounds); 642 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 643 finishRender(); 644 645 return mDrawn; 646} 647 648void FontRenderer::removeFont(const Font* font) { 649 mActiveFonts.remove(font->getDescription()); 650 651 if (mCurrentFont == font) { 652 mCurrentFont = NULL; 653 } 654} 655 656void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { 657 // Compute gaussian weights for the blur 658 // e is the euler's number 659 float e = 2.718281828459045f; 660 float pi = 3.1415926535897932f; 661 // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) 662 // x is of the form [-radius .. 0 .. radius] 663 // and sigma varies with radius. 664 // Based on some experimental radius values and sigma's 665 // we approximately fit sigma = f(radius) as 666 // sigma = radius * 0.3 + 0.6 667 // The larger the radius gets, the more our gaussian blur 668 // will resemble a box blur since with large sigma 669 // the gaussian curve begins to lose its shape 670 float sigma = 0.3f * (float) radius + 0.6f; 671 672 // Now compute the coefficints 673 // We will store some redundant values to save some math during 674 // the blur calculations 675 // precompute some values 676 float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); 677 float coeff2 = - 1.0f / (2.0f * sigma * sigma); 678 679 float normalizeFactor = 0.0f; 680 for (int32_t r = -radius; r <= radius; r ++) { 681 float floatR = (float) r; 682 weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); 683 normalizeFactor += weights[r + radius]; 684 } 685 686 //Now we need to normalize the weights because all our coefficients need to add up to one 687 normalizeFactor = 1.0f / normalizeFactor; 688 for (int32_t r = -radius; r <= radius; r ++) { 689 weights[r + radius] *= normalizeFactor; 690 } 691} 692 693void FontRenderer::horizontalBlur(float* weights, int32_t radius, 694 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 695 float blurredPixel = 0.0f; 696 float currentPixel = 0.0f; 697 698 for (int32_t y = 0; y < height; y ++) { 699 700 const uint8_t* input = source + y * width; 701 uint8_t* output = dest + y * width; 702 703 for (int32_t x = 0; x < width; x ++) { 704 blurredPixel = 0.0f; 705 const float* gPtr = weights; 706 // Optimization for non-border pixels 707 if (x > radius && x < (width - radius)) { 708 const uint8_t *i = input + (x - radius); 709 for (int r = -radius; r <= radius; r ++) { 710 currentPixel = (float) (*i); 711 blurredPixel += currentPixel * gPtr[0]; 712 gPtr++; 713 i++; 714 } 715 } else { 716 for (int32_t r = -radius; r <= radius; r ++) { 717 // Stepping left and right away from the pixel 718 int validW = x + r; 719 if (validW < 0) { 720 validW = 0; 721 } 722 if (validW > width - 1) { 723 validW = width - 1; 724 } 725 726 currentPixel = (float) input[validW]; 727 blurredPixel += currentPixel * gPtr[0]; 728 gPtr++; 729 } 730 } 731 *output = (uint8_t)blurredPixel; 732 output ++; 733 } 734 } 735} 736 737void FontRenderer::verticalBlur(float* weights, int32_t radius, 738 const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { 739 float blurredPixel = 0.0f; 740 float currentPixel = 0.0f; 741 742 for (int32_t y = 0; y < height; y ++) { 743 uint8_t* output = dest + y * width; 744 745 for (int32_t x = 0; x < width; x ++) { 746 blurredPixel = 0.0f; 747 const float* gPtr = weights; 748 const uint8_t* input = source + x; 749 // Optimization for non-border pixels 750 if (y > radius && y < (height - radius)) { 751 const uint8_t *i = input + ((y - radius) * width); 752 for (int32_t r = -radius; r <= radius; r ++) { 753 currentPixel = (float)(*i); 754 blurredPixel += currentPixel * gPtr[0]; 755 gPtr++; 756 i += width; 757 } 758 } else { 759 for (int32_t r = -radius; r <= radius; r ++) { 760 int validH = y + r; 761 // Clamp to zero and width 762 if (validH < 0) { 763 validH = 0; 764 } 765 if (validH > height - 1) { 766 validH = height - 1; 767 } 768 769 const uint8_t *i = input + validH * width; 770 currentPixel = (float) (*i); 771 blurredPixel += currentPixel * gPtr[0]; 772 gPtr++; 773 } 774 } 775 *output = (uint8_t) blurredPixel; 776 output++; 777 } 778 } 779} 780 781void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { 782 if (width * height * radius < RS_MIN_INPUT_CUTOFF) { 783 float *gaussian = new float[2 * radius + 1]; 784 computeGaussianWeights(gaussian, radius); 785 786 uint8_t* scratch = new uint8_t[width * height]; 787 788 horizontalBlur(gaussian, radius, *image, scratch, width, height); 789 verticalBlur(gaussian, radius, scratch, *image, width, height); 790 791 delete[] gaussian; 792 delete[] scratch; 793 return; 794 } 795 796 uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 797 798 if (mRs.get() == 0) { 799 mRs = new RSC::RS(); 800 if (!mRs->init(true, true)) { 801 ALOGE("blur RS failed to init"); 802 } 803 804 mRsElement = RSC::Element::A_8(mRs); 805 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement); 806 } 807 808 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 809 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, 810 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); 811 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, 812 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); 813 814 mRsScript->setRadius(radius); 815 mRsScript->blur(ain, aout); 816 817 // replace the original image's pointer, avoiding a copy back to the original buffer 818 free(*image); 819 *image = outImage; 820} 821 822}; // namespace uirenderer 823}; // namespace android 824