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