FontRenderer.cpp revision 115096f50a560e64a7f95d37686d4861042c7aeb
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 uint16_t* offset = (uint16_t*)(mLastQuadIndex * sizeof(uint16_t) * 6); 382 uint32_t count = mCurrentQuadIndex - mLastQuadIndex; 383 mDrawBatch.add(TextBatch(offset, count, 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 caches.activeTexture(0); 442 GLuint lastId = 0; 443 444 for (uint32_t i = 0; i < mDrawBatch.size(); i++) { 445 const TextBatch& batch = mDrawBatch[i]; 446 447 GLuint id = batch.texture->getTextureId(); 448 if (id != lastId) { 449 glBindTexture(GL_TEXTURE_2D, id); 450 batch.texture->setLinearFiltering(mLinearFiltering, false); 451 lastId = id; 452 } 453 454 glDrawElements(GL_TRIANGLES, batch.count * 6, GL_UNSIGNED_SHORT, batch.offset); 455 } 456 457 mDrawn = true; 458 459 mCurrentQuadIndex = 0; 460 mLastQuadIndex = 0; 461 mDrawBatch.clear(); 462} 463 464void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 465 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 466 float x4, float y4, float u4, float v4, CacheTexture* texture) { 467 if (texture != mCurrentCacheTexture) { 468 updateDrawParams(); 469 // Now use the new texture id 470 mCurrentCacheTexture = texture; 471 } 472 473 const uint32_t vertsPerQuad = 4; 474 const uint32_t floatsPerVert = 4; 475 float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; 476 477 (*currentPos++) = x1; 478 (*currentPos++) = y1; 479 (*currentPos++) = u1; 480 (*currentPos++) = v1; 481 482 (*currentPos++) = x2; 483 (*currentPos++) = y2; 484 (*currentPos++) = u2; 485 (*currentPos++) = v2; 486 487 (*currentPos++) = x3; 488 (*currentPos++) = y3; 489 (*currentPos++) = u3; 490 (*currentPos++) = v3; 491 492 (*currentPos++) = x4; 493 (*currentPos++) = y4; 494 (*currentPos++) = u4; 495 (*currentPos++) = v4; 496 497 mCurrentQuadIndex++; 498} 499 500void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 501 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 502 float x4, float y4, float u4, float v4, CacheTexture* texture) { 503 504 if (mClip && 505 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 506 return; 507 } 508 509 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 510 511 if (mBounds) { 512 mBounds->left = fmin(mBounds->left, x1); 513 mBounds->top = fmin(mBounds->top, y3); 514 mBounds->right = fmax(mBounds->right, x3); 515 mBounds->bottom = fmax(mBounds->bottom, y1); 516 } 517 518 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 519 issueDrawCommand(); 520 } 521} 522 523void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 524 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 525 float x4, float y4, float u4, float v4, CacheTexture* texture) { 526 527 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 528 529 if (mBounds) { 530 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 531 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 532 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 533 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 534 } 535 536 if (mCurrentQuadIndex == mMaxNumberOfQuads) { 537 issueDrawCommand(); 538 } 539} 540 541void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) { 542 mCurrentFont = Font::create(this, paint, matrix); 543} 544 545FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, 546 uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { 547 checkInit(); 548 549 if (!mCurrentFont) { 550 DropShadow image; 551 image.width = 0; 552 image.height = 0; 553 image.image = NULL; 554 image.penX = 0; 555 image.penY = 0; 556 return image; 557 } 558 559 mDrawn = false; 560 mClip = NULL; 561 mBounds = NULL; 562 563 Rect bounds; 564 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 565 566 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; 567 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; 568 569 // Align buffers for renderscript usage 570 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 571 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 572 } 573 574 int size = paddedWidth * paddedHeight; 575 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 576 memset(dataBuffer, 0, size); 577 578 int penX = radius - bounds.left; 579 int penY = radius - bounds.bottom; 580 581 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 582 // text has non-whitespace, so draw and blur to create the shadow 583 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 584 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 585 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 586 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions); 587 588 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 589 } 590 591 DropShadow image; 592 image.width = paddedWidth; 593 image.height = paddedHeight; 594 image.image = dataBuffer; 595 image.penX = penX; 596 image.penY = penY; 597 598 return image; 599} 600 601void FontRenderer::initRender(const Rect* clip, Rect* bounds) { 602 checkInit(); 603 604 mDrawn = false; 605 mBounds = bounds; 606 mClip = clip; 607} 608 609void FontRenderer::finishRender() { 610 mBounds = NULL; 611 mClip = NULL; 612 613 if (mCurrentQuadIndex != 0) { 614 issueDrawCommand(); 615 } 616} 617 618void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) { 619 Font* font = Font::create(this, paint, matrix); 620 font->precache(paint, text, numGlyphs); 621} 622 623bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, 624 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 625 const float* positions, Rect* bounds) { 626 if (!mCurrentFont) { 627 ALOGE("No font set"); 628 return false; 629 } 630 631 initRender(clip, bounds); 632 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 633 finishRender(); 634 635 return mDrawn; 636} 637 638bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, 639 uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, 640 float hOffset, float vOffset, Rect* bounds) { 641 if (!mCurrentFont) { 642 ALOGE("No font set"); 643 return false; 644 } 645 646 initRender(clip, bounds); 647 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 648 finishRender(); 649 650 return mDrawn; 651} 652 653void FontRenderer::removeFont(const Font* font) { 654 mActiveFonts.remove(font->getDescription()); 655 656 if (mCurrentFont == font) { 657 mCurrentFont = NULL; 658 } 659} 660 661void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { 662 if (width * height * radius < RS_MIN_INPUT_CUTOFF) { 663 float *gaussian = new float[2 * radius + 1]; 664 Blur::generateGaussianWeights(gaussian, radius); 665 666 uint8_t* scratch = new uint8_t[width * height]; 667 Blur::horizontal(gaussian, radius, *image, scratch, width, height); 668 Blur::vertical(gaussian, radius, scratch, *image, width, height); 669 670 delete[] gaussian; 671 delete[] scratch; 672 return; 673 } 674 675 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 676 677 if (mRs.get() == 0) { 678 mRs = new RSC::RS(); 679 if (!mRs->init(true, true)) { 680 ALOGE("blur RS failed to init"); 681 } 682 683 mRsElement = RSC::Element::A_8(mRs); 684 mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement); 685 } 686 687 sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 688 sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, 689 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); 690 sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, 691 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); 692 693 mRsScript->setRadius(radius); 694 mRsScript->blur(ain, aout); 695 696 // replace the original image's pointer, avoiding a copy back to the original buffer 697 free(*image); 698 *image = outImage; 699} 700 701}; // namespace uirenderer 702}; // namespace android 703