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