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