FontRenderer.cpp revision e2bb380bc26749782c873e5488cfdf4e42b27346
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#include "FontRenderer.h" 18 19#include "Caches.h" 20#include "Debug.h" 21#include "Extensions.h" 22#include "Glop.h" 23#include "GlopBuilder.h" 24#include "OpenGLRenderer.h" 25#include "PixelBuffer.h" 26#include "Rect.h" 27#include "renderstate/RenderState.h" 28#include "utils/Blur.h" 29#include "utils/MathUtils.h" 30#include "utils/Timing.h" 31 32#include <SkGlyph.h> 33#include <SkUtils.h> 34 35#include <cutils/properties.h> 36 37#include <utils/Log.h> 38 39#ifdef ANDROID_ENABLE_RENDERSCRIPT 40#include <RenderScript.h> 41#endif 42 43namespace android { 44namespace uirenderer { 45 46// blur inputs smaller than this constant will bypass renderscript 47#define RS_MIN_INPUT_CUTOFF 10000 48 49#define USE_GLOPS true 50 51/////////////////////////////////////////////////////////////////////////////// 52// TextSetupFunctor 53/////////////////////////////////////////////////////////////////////////////// 54void TextSetupFunctor::setup(GLenum glyphFormat) { 55 renderer->setupDraw(); 56 renderer->setupDrawTextGamma(paint); 57 renderer->setupDrawDirtyRegionsDisabled(); 58 renderer->setupDrawWithTexture(glyphFormat == GL_ALPHA); 59 switch (glyphFormat) { 60 case GL_ALPHA: { 61 renderer->setupDrawAlpha8Color(paint->getColor(), alpha); 62 break; 63 } 64 case GL_RGBA: { 65 float floatAlpha = alpha / 255.0f; 66 renderer->setupDrawColor(floatAlpha, floatAlpha, floatAlpha, floatAlpha); 67 break; 68 } 69 default: { 70#if DEBUG_FONT_RENDERER 71 ALOGD("TextSetupFunctor: called with unknown glyph format %x", glyphFormat); 72#endif 73 break; 74 } 75 } 76 renderer->setupDrawColorFilter(paint->getColorFilter()); 77 renderer->setupDrawShader(paint->getShader()); 78 renderer->setupDrawBlending(paint); 79 renderer->setupDrawProgram(); 80 renderer->setupDrawModelView(kModelViewMode_Translate, false, 81 0.0f, 0.0f, 0.0f, 0.0f, pureTranslate); 82 // Calling setupDrawTexture with the name 0 will enable the 83 // uv attributes and increase the texture unit count 84 // texture binding will be performed by the font renderer as 85 // needed 86 renderer->setupDrawTexture(0); 87 renderer->setupDrawPureColorUniforms(); 88 renderer->setupDrawColorFilterUniforms(paint->getColorFilter()); 89 renderer->setupDrawShaderUniforms(paint->getShader(), pureTranslate); 90 renderer->setupDrawTextGammaUniforms(); 91} 92 93void TextSetupFunctor::draw(CacheTexture& texture, bool linearFiltering) { 94 int textureFillFlags = static_cast<int>(texture.getFormat() == GL_ALPHA 95 ? TextureFillFlags::kIsAlphaMaskTexture : TextureFillFlags::kNone); 96 if (linearFiltering) { 97 textureFillFlags |= TextureFillFlags::kForceFilter; 98 } 99 const Matrix4& transform = pureTranslate ? Matrix4::identity() : *(renderer->currentTransform()); 100 Glop glop; 101 GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop) 102 .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) 103 .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha) 104 .setTransform(renderer->currentSnapshot()->getOrthoMatrix(), transform, false) 105 .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) 106 .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState) 107 .build(); 108 renderer->renderGlop(glop); 109} 110 111/////////////////////////////////////////////////////////////////////////////// 112// FontRenderer 113/////////////////////////////////////////////////////////////////////////////// 114 115static bool sLogFontRendererCreate = true; 116 117FontRenderer::FontRenderer() 118 : mGammaTable(nullptr) 119 , mCurrentFont(nullptr) 120 , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) 121 , mCurrentCacheTexture(nullptr) 122 , mUploadTexture(false) 123 , mFunctor(nullptr) 124 , mClip(nullptr) 125 , mBounds(nullptr) 126 , mDrawn(false) 127 , mInitialized(false) 128 , mLinearFiltering(false) { 129 130 if (sLogFontRendererCreate) { 131 INIT_LOGD("Creating FontRenderer"); 132 } 133 134 mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH; 135 mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT; 136 mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH; 137 mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT; 138 139 char property[PROPERTY_VALUE_MAX]; 140 if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, nullptr) > 0) { 141 mSmallCacheWidth = atoi(property); 142 } 143 144 if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, nullptr) > 0) { 145 mSmallCacheHeight = atoi(property); 146 } 147 148 if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, nullptr) > 0) { 149 mLargeCacheWidth = atoi(property); 150 } 151 152 if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, nullptr) > 0) { 153 mLargeCacheHeight = atoi(property); 154 } 155 156 uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize; 157 158 mSmallCacheWidth = MathUtils::min(mSmallCacheWidth, maxTextureSize); 159 mSmallCacheHeight = MathUtils::min(mSmallCacheHeight, maxTextureSize); 160 mLargeCacheWidth = MathUtils::min(mLargeCacheWidth, maxTextureSize); 161 mLargeCacheHeight = MathUtils::min(mLargeCacheHeight, maxTextureSize); 162 163 if (sLogFontRendererCreate) { 164 INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", 165 mSmallCacheWidth, mSmallCacheHeight, 166 mLargeCacheWidth, mLargeCacheHeight >> 1, 167 mLargeCacheWidth, mLargeCacheHeight >> 1, 168 mLargeCacheWidth, mLargeCacheHeight); 169 } 170 171 sLogFontRendererCreate = false; 172} 173 174void clearCacheTextures(Vector<CacheTexture*>& cacheTextures) { 175 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 176 delete cacheTextures[i]; 177 } 178 cacheTextures.clear(); 179} 180 181FontRenderer::~FontRenderer() { 182 clearCacheTextures(mACacheTextures); 183 clearCacheTextures(mRGBACacheTextures); 184 185 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 186 while (it.next()) { 187 delete it.value(); 188 } 189 mActiveFonts.clear(); 190} 191 192void FontRenderer::flushAllAndInvalidate() { 193 issueDrawCommand(); 194 195 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 196 while (it.next()) { 197 it.value()->invalidateTextureCache(); 198 } 199 200 for (uint32_t i = 0; i < mACacheTextures.size(); i++) { 201 mACacheTextures[i]->init(); 202 } 203 204 for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { 205 mRGBACacheTextures[i]->init(); 206 } 207} 208 209void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) { 210 // Start from 1; don't deallocate smallest/default texture 211 for (uint32_t i = 1; i < cacheTextures.size(); i++) { 212 CacheTexture* cacheTexture = cacheTextures[i]; 213 if (cacheTexture->getPixelBuffer()) { 214 cacheTexture->init(); 215 LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); 216 while (it.next()) { 217 it.value()->invalidateTextureCache(cacheTexture); 218 } 219 cacheTexture->releasePixelBuffer(); 220 } 221 } 222} 223 224void FontRenderer::flushLargeCaches() { 225 flushLargeCaches(mACacheTextures); 226 flushLargeCaches(mRGBACacheTextures); 227} 228 229CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures, 230 const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) { 231 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 232 if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) { 233 return cacheTextures[i]; 234 } 235 } 236 // Could not fit glyph into current cache textures 237 return nullptr; 238} 239 240void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, 241 uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { 242 checkInit(); 243 244 // If the glyph bitmap is empty let's assum the glyph is valid 245 // so we can avoid doing extra work later on 246 if (glyph.fWidth == 0 || glyph.fHeight == 0) { 247 cachedGlyph->mIsValid = true; 248 cachedGlyph->mCacheTexture = nullptr; 249 return; 250 } 251 252 cachedGlyph->mIsValid = false; 253 254 // choose an appropriate cache texture list for this glyph format 255 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); 256 Vector<CacheTexture*>* cacheTextures = nullptr; 257 switch (format) { 258 case SkMask::kA8_Format: 259 case SkMask::kBW_Format: 260 cacheTextures = &mACacheTextures; 261 break; 262 case SkMask::kARGB32_Format: 263 cacheTextures = &mRGBACacheTextures; 264 break; 265 default: 266#if DEBUG_FONT_RENDERER 267 ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format); 268#endif 269 return; 270 } 271 272 // If the glyph is too tall, don't cache it 273 if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > 274 (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) { 275 ALOGE("Font size too large to fit in cache. width, height = %i, %i", 276 (int) glyph.fWidth, (int) glyph.fHeight); 277 return; 278 } 279 280 // Now copy the bitmap into the cache texture 281 uint32_t startX = 0; 282 uint32_t startY = 0; 283 284 CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY); 285 286 if (!cacheTexture) { 287 if (!precaching) { 288 // If the new glyph didn't fit and we are not just trying to precache it, 289 // clear out the cache and try again 290 flushAllAndInvalidate(); 291 cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY); 292 } 293 294 if (!cacheTexture) { 295 // either the glyph didn't fit or we're precaching and will cache it when we draw 296 return; 297 } 298 } 299 300 cachedGlyph->mCacheTexture = cacheTexture; 301 302 *retOriginX = startX; 303 *retOriginY = startY; 304 305 uint32_t endX = startX + glyph.fWidth; 306 uint32_t endY = startY + glyph.fHeight; 307 308 uint32_t cacheWidth = cacheTexture->getWidth(); 309 310 if (!cacheTexture->getPixelBuffer()) { 311 Caches::getInstance().textureState().activateTexture(0); 312 // Large-glyph texture memory is allocated only as needed 313 cacheTexture->allocatePixelBuffer(); 314 } 315 if (!cacheTexture->mesh()) { 316 cacheTexture->allocateMesh(); 317 } 318 319 uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map(); 320 uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; 321 int srcStride = glyph.rowBytes(); 322 323 // Copy the glyph image, taking the mask format into account 324 switch (format) { 325 case SkMask::kA8_Format: { 326 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; 327 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX 328 - TEXTURE_BORDER_SIZE; 329 // write leading border line 330 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 331 // write glyph data 332 if (mGammaTable) { 333 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { 334 row = cacheY * cacheWidth; 335 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 336 for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { 337 uint8_t tempCol = bitmapBuffer[bY + bX]; 338 cacheBuffer[row + cacheX] = mGammaTable[tempCol]; 339 } 340 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 341 } 342 } else { 343 for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { 344 row = cacheY * cacheWidth; 345 memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth); 346 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 347 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 348 } 349 } 350 // write trailing border line 351 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; 352 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 353 break; 354 } 355 case SkMask::kARGB32_Format: { 356 // prep data lengths 357 const size_t formatSize = PixelBuffer::formatSize(GL_RGBA); 358 const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE; 359 size_t rowSize = formatSize * glyph.fWidth; 360 // prep advances 361 size_t dstStride = formatSize * cacheWidth; 362 // prep indices 363 // - we actually start one row early, and then increment before first copy 364 uint8_t* src = &bitmapBuffer[0 - srcStride]; 365 uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)]; 366 uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)]; 367 uint8_t* dstL = dst - borderSize; 368 uint8_t* dstR = dst + rowSize; 369 // write leading border line 370 memset(dstL, 0, rowSize + 2 * borderSize); 371 // write glyph data 372 while (dst < dstEnd) { 373 memset(dstL += dstStride, 0, borderSize); // leading border column 374 memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data 375 memset(dstR += dstStride, 0, borderSize); // trailing border column 376 } 377 // write trailing border line 378 memset(dstL += dstStride, 0, rowSize + 2 * borderSize); 379 break; 380 } 381 case SkMask::kBW_Format: { 382 uint32_t cacheX = 0, cacheY = 0; 383 uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX 384 - TEXTURE_BORDER_SIZE; 385 static const uint8_t COLORS[2] = { 0, 255 }; 386 // write leading border line 387 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 388 // write glyph data 389 for (cacheY = startY; cacheY < endY; cacheY++) { 390 cacheX = startX; 391 int rowBytes = srcStride; 392 uint8_t* buffer = bitmapBuffer; 393 394 row = cacheY * cacheWidth; 395 cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; 396 while (--rowBytes >= 0) { 397 uint8_t b = *buffer++; 398 for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) { 399 cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1]; 400 } 401 } 402 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; 403 404 bitmapBuffer += srcStride; 405 } 406 // write trailing border line 407 row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; 408 memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); 409 break; 410 } 411 default: 412 ALOGW("Unknown glyph format: 0x%x", format); 413 break; 414 } 415 416 cachedGlyph->mIsValid = true; 417} 418 419CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, 420 bool allocate) { 421 CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads); 422 423 if (allocate) { 424 Caches::getInstance().textureState().activateTexture(0); 425 cacheTexture->allocatePixelBuffer(); 426 cacheTexture->allocateMesh(); 427 } 428 429 return cacheTexture; 430} 431 432void FontRenderer::initTextTexture() { 433 clearCacheTextures(mACacheTextures); 434 clearCacheTextures(mRGBACacheTextures); 435 436 mUploadTexture = false; 437 mACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 438 GL_ALPHA, true)); 439 mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 440 GL_ALPHA, false)); 441 mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 442 GL_ALPHA, false)); 443 mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, 444 GL_ALPHA, false)); 445 mRGBACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, 446 GL_RGBA, false)); 447 mRGBACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, 448 GL_RGBA, false)); 449 mCurrentCacheTexture = mACacheTextures[0]; 450} 451 452// We don't want to allocate anything unless we actually draw text 453void FontRenderer::checkInit() { 454 if (mInitialized) { 455 return; 456 } 457 458 initTextTexture(); 459 460 mInitialized = true; 461} 462 463void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheTextures, 464 bool& resetPixelStore, GLuint& lastTextureId) { 465 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 466 CacheTexture* cacheTexture = cacheTextures[i]; 467 if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) { 468 if (cacheTexture->getTextureId() != lastTextureId) { 469 lastTextureId = cacheTexture->getTextureId(); 470 caches.textureState().activateTexture(0); 471 caches.textureState().bindTexture(lastTextureId); 472 } 473 474 if (cacheTexture->upload()) { 475 resetPixelStore = true; 476 } 477 } 478 } 479} 480 481void FontRenderer::checkTextureUpdate() { 482 if (!mUploadTexture) { 483 return; 484 } 485 486 Caches& caches = Caches::getInstance(); 487 GLuint lastTextureId = 0; 488 489 bool resetPixelStore = false; 490 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 491 492 // Iterate over all the cache textures and see which ones need to be updated 493 checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId); 494 checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId); 495 496 // Unbind any PBO we might have used to update textures 497 caches.pixelBufferState().unbind(); 498 499 // Reset to default unpack row length to avoid affecting texture 500 // uploads in other parts of the renderer 501 if (resetPixelStore) { 502 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 503 } 504 505 mUploadTexture = false; 506} 507 508void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { 509 if (!mFunctor) return; 510 511 Caches& caches = mFunctor->renderer->getCaches(); 512 RenderState& renderState = mFunctor->renderer->renderState(); 513 514 bool first = true; 515 bool forceRebind = false; 516 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 517 CacheTexture* texture = cacheTextures[i]; 518 if (texture->canDraw()) { 519 if (first) { 520 checkTextureUpdate(); 521#if !USE_GLOPS 522 mFunctor->setup(texture->getFormat()); 523 524 renderState.meshState().bindQuadIndicesBuffer(); 525 526 // If returns true, a VBO was bound and we must 527 // rebind our vertex attrib pointers even if 528 // they have the same values as the current pointers 529 forceRebind = renderState.meshState().unbindMeshBuffer(); 530 531 caches.textureState().activateTexture(0); 532#endif 533 first = false; 534 mDrawn = true; 535 } 536#if USE_GLOPS 537 mFunctor->draw(*texture, mLinearFiltering); 538#endif 539 540#if !USE_GLOPS 541 caches.textureState().bindTexture(texture->getTextureId()); 542 texture->setLinearFiltering(mLinearFiltering); 543 544 TextureVertex* mesh = texture->mesh(); 545 MeshState& meshState = renderState.meshState(); 546 meshState.bindPositionVertexPointer(forceRebind, &mesh[0].x); 547 meshState.bindTexCoordsVertexPointer(forceRebind, &mesh[0].u); 548 549 glDrawElements(GL_TRIANGLES, texture->meshElementCount(), 550 GL_UNSIGNED_SHORT, texture->indices()); 551#endif 552 texture->resetMesh(); 553 forceRebind = false; 554 } 555 } 556} 557 558void FontRenderer::issueDrawCommand() { 559 issueDrawCommand(mACacheTextures); 560 issueDrawCommand(mRGBACacheTextures); 561} 562 563void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 564 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 565 float x4, float y4, float u4, float v4, CacheTexture* texture) { 566 if (texture != mCurrentCacheTexture) { 567 // Now use the new texture id 568 mCurrentCacheTexture = texture; 569 } 570 571 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, 572 x3, y3, u3, v3, x4, y4, u4, v4); 573} 574 575void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 576 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 577 float x4, float y4, float u4, float v4, CacheTexture* texture) { 578 579 if (mClip && 580 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 581 return; 582 } 583 584 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 585 586 if (mBounds) { 587 mBounds->left = fmin(mBounds->left, x1); 588 mBounds->top = fmin(mBounds->top, y3); 589 mBounds->right = fmax(mBounds->right, x3); 590 mBounds->bottom = fmax(mBounds->bottom, y1); 591 } 592 593 if (mCurrentCacheTexture->endOfMesh()) { 594 issueDrawCommand(); 595 } 596} 597 598void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 599 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 600 float x4, float y4, float u4, float v4, CacheTexture* texture) { 601 602 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 603 604 if (mBounds) { 605 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 606 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 607 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 608 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 609 } 610 611 if (mCurrentCacheTexture->endOfMesh()) { 612 issueDrawCommand(); 613 } 614} 615 616void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { 617 mCurrentFont = Font::create(this, paint, matrix); 618} 619 620FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, 621 uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) { 622 checkInit(); 623 624 DropShadow image; 625 image.width = 0; 626 image.height = 0; 627 image.image = nullptr; 628 image.penX = 0; 629 image.penY = 0; 630 631 if (!mCurrentFont) { 632 return image; 633 } 634 635 mDrawn = false; 636 mClip = nullptr; 637 mBounds = nullptr; 638 639 Rect bounds; 640 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 641 642 uint32_t intRadius = Blur::convertRadiusToInt(radius); 643 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; 644 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; 645 646 uint32_t maxSize = Caches::getInstance().maxTextureSize; 647 if (paddedWidth > maxSize || paddedHeight > maxSize) { 648 return image; 649 } 650 651#ifdef ANDROID_ENABLE_RENDERSCRIPT 652 // Align buffers for renderscript usage 653 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 654 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 655 } 656 int size = paddedWidth * paddedHeight; 657 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 658#else 659 int size = paddedWidth * paddedHeight; 660 uint8_t* dataBuffer = (uint8_t*) malloc(size); 661#endif 662 663 memset(dataBuffer, 0, size); 664 665 int penX = intRadius - bounds.left; 666 int penY = intRadius - bounds.bottom; 667 668 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 669 // text has non-whitespace, so draw and blur to create the shadow 670 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 671 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 672 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 673 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); 674 675 // Unbind any PBO we might have used 676 Caches::getInstance().pixelBufferState().unbind(); 677 678 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 679 } 680 681 image.width = paddedWidth; 682 image.height = paddedHeight; 683 image.image = dataBuffer; 684 image.penX = penX; 685 image.penY = penY; 686 687 return image; 688} 689 690void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextSetupFunctor* functor) { 691 checkInit(); 692 693 mDrawn = false; 694 mBounds = bounds; 695 mFunctor = functor; 696 mClip = clip; 697} 698 699void FontRenderer::finishRender() { 700 mBounds = nullptr; 701 mClip = nullptr; 702 703 issueDrawCommand(); 704} 705 706void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, 707 const SkMatrix& matrix) { 708 Font* font = Font::create(this, paint, matrix); 709 font->precache(paint, text, numGlyphs); 710} 711 712void FontRenderer::endPrecaching() { 713 checkTextureUpdate(); 714} 715 716bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, 717 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 718 const float* positions, Rect* bounds, TextSetupFunctor* functor, bool forceFinish) { 719 if (!mCurrentFont) { 720 ALOGE("No font set"); 721 return false; 722 } 723 724 initRender(clip, bounds, functor); 725 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 726 727 if (forceFinish) { 728 finishRender(); 729 } 730 731 return mDrawn; 732} 733 734bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, 735 uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, 736 float hOffset, float vOffset, Rect* bounds, TextSetupFunctor* functor) { 737 if (!mCurrentFont) { 738 ALOGE("No font set"); 739 return false; 740 } 741 742 initRender(clip, bounds, functor); 743 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 744 finishRender(); 745 746 return mDrawn; 747} 748 749void FontRenderer::removeFont(const Font* font) { 750 mActiveFonts.remove(font->getDescription()); 751 752 if (mCurrentFont == font) { 753 mCurrentFont = nullptr; 754 } 755} 756 757void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { 758 uint32_t intRadius = Blur::convertRadiusToInt(radius); 759#ifdef ANDROID_ENABLE_RENDERSCRIPT 760 if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { 761 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 762 763 if (mRs == nullptr) { 764 mRs = new RSC::RS(); 765 // a null path is OK because there are no custom kernels used 766 // hence nothing gets cached by RS 767 if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { 768 mRs.clear(); 769 ALOGE("blur RS failed to init"); 770 } else { 771 mRsElement = RSC::Element::A_8(mRs); 772 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); 773 } 774 } 775 if (mRs != nullptr) { 776 RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 777 RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, 778 RS_ALLOCATION_MIPMAP_NONE, 779 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 780 *image); 781 RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, 782 RS_ALLOCATION_MIPMAP_NONE, 783 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 784 outImage); 785 786 mRsScript->setRadius(radius); 787 mRsScript->setInput(ain); 788 mRsScript->forEach(aout); 789 790 // replace the original image's pointer, avoiding a copy back to the original buffer 791 free(*image); 792 *image = outImage; 793 794 return; 795 } 796 } 797#endif 798 799 std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); 800 Blur::generateGaussianWeights(gaussian.get(), intRadius); 801 802 std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); 803 Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); 804 Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); 805} 806 807static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) { 808 uint32_t size = 0; 809 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 810 CacheTexture* cacheTexture = cacheTextures[i]; 811 if (cacheTexture && cacheTexture->getPixelBuffer()) { 812 size += cacheTexture->getPixelBuffer()->getSize(); 813 } 814 } 815 return size; 816} 817 818uint32_t FontRenderer::getCacheSize(GLenum format) const { 819 switch (format) { 820 case GL_ALPHA: { 821 return calculateCacheSize(mACacheTextures); 822 } 823 case GL_RGBA: { 824 return calculateCacheSize(mRGBACacheTextures); 825 } 826 default: { 827 return 0; 828 } 829 } 830} 831 832}; // namespace uirenderer 833}; // namespace android 834