FontRenderer.cpp revision c6128bae4312aca7902589bda793a7ff89810401
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#if !USE_GLOPS 512 Caches& caches = mFunctor->renderer->getCaches(); 513 RenderState& renderState = mFunctor->renderer->renderState(); 514#endif 515 516 bool first = true; 517 bool forceRebind = false; 518 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 519 CacheTexture* texture = cacheTextures[i]; 520 if (texture->canDraw()) { 521 if (first) { 522 checkTextureUpdate(); 523#if !USE_GLOPS 524 mFunctor->setup(texture->getFormat()); 525 526 renderState.meshState().bindQuadIndicesBuffer(); 527 528 // If returns true, a VBO was bound and we must 529 // rebind our vertex attrib pointers even if 530 // they have the same values as the current pointers 531 forceRebind = renderState.meshState().unbindMeshBuffer(); 532 533 caches.textureState().activateTexture(0); 534#endif 535 first = false; 536 mDrawn = true; 537 } 538#if USE_GLOPS 539 mFunctor->draw(*texture, mLinearFiltering); 540#endif 541 542#if !USE_GLOPS 543 caches.textureState().bindTexture(texture->getTextureId()); 544 texture->setLinearFiltering(mLinearFiltering); 545 546 TextureVertex* mesh = texture->mesh(); 547 MeshState& meshState = renderState.meshState(); 548 meshState.bindPositionVertexPointer(forceRebind, &mesh[0].x); 549 meshState.bindTexCoordsVertexPointer(forceRebind, &mesh[0].u); 550 551 glDrawElements(GL_TRIANGLES, texture->meshElementCount(), 552 GL_UNSIGNED_SHORT, texture->indices()); 553#endif 554 texture->resetMesh(); 555 forceRebind = false; 556 } 557 } 558} 559 560void FontRenderer::issueDrawCommand() { 561 issueDrawCommand(mACacheTextures); 562 issueDrawCommand(mRGBACacheTextures); 563} 564 565void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, 566 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 567 float x4, float y4, float u4, float v4, CacheTexture* texture) { 568 if (texture != mCurrentCacheTexture) { 569 // Now use the new texture id 570 mCurrentCacheTexture = texture; 571 } 572 573 mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, 574 x3, y3, u3, v3, x4, y4, u4, v4); 575} 576 577void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, 578 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 579 float x4, float y4, float u4, float v4, CacheTexture* texture) { 580 581 if (mClip && 582 (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { 583 return; 584 } 585 586 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 587 588 if (mBounds) { 589 mBounds->left = fmin(mBounds->left, x1); 590 mBounds->top = fmin(mBounds->top, y3); 591 mBounds->right = fmax(mBounds->right, x3); 592 mBounds->bottom = fmax(mBounds->bottom, y1); 593 } 594 595 if (mCurrentCacheTexture->endOfMesh()) { 596 issueDrawCommand(); 597 } 598} 599 600void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, 601 float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, 602 float x4, float y4, float u4, float v4, CacheTexture* texture) { 603 604 appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); 605 606 if (mBounds) { 607 mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); 608 mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); 609 mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); 610 mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); 611 } 612 613 if (mCurrentCacheTexture->endOfMesh()) { 614 issueDrawCommand(); 615 } 616} 617 618void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { 619 mCurrentFont = Font::create(this, paint, matrix); 620} 621 622FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, 623 uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) { 624 checkInit(); 625 626 DropShadow image; 627 image.width = 0; 628 image.height = 0; 629 image.image = nullptr; 630 image.penX = 0; 631 image.penY = 0; 632 633 if (!mCurrentFont) { 634 return image; 635 } 636 637 mDrawn = false; 638 mClip = nullptr; 639 mBounds = nullptr; 640 641 Rect bounds; 642 mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); 643 644 uint32_t intRadius = Blur::convertRadiusToInt(radius); 645 uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; 646 uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; 647 648 uint32_t maxSize = Caches::getInstance().maxTextureSize; 649 if (paddedWidth > maxSize || paddedHeight > maxSize) { 650 return image; 651 } 652 653#ifdef ANDROID_ENABLE_RENDERSCRIPT 654 // Align buffers for renderscript usage 655 if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { 656 paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; 657 } 658 int size = paddedWidth * paddedHeight; 659 uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); 660#else 661 int size = paddedWidth * paddedHeight; 662 uint8_t* dataBuffer = (uint8_t*) malloc(size); 663#endif 664 665 memset(dataBuffer, 0, size); 666 667 int penX = intRadius - bounds.left; 668 int penY = intRadius - bounds.bottom; 669 670 if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { 671 // text has non-whitespace, so draw and blur to create the shadow 672 // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted 673 // TODO: don't draw pure whitespace in the first place, and avoid needing this check 674 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, 675 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); 676 677 // Unbind any PBO we might have used 678 Caches::getInstance().pixelBufferState().unbind(); 679 680 blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); 681 } 682 683 image.width = paddedWidth; 684 image.height = paddedHeight; 685 image.image = dataBuffer; 686 image.penX = penX; 687 image.penY = penY; 688 689 return image; 690} 691 692void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextSetupFunctor* functor) { 693 checkInit(); 694 695 mDrawn = false; 696 mBounds = bounds; 697 mFunctor = functor; 698 mClip = clip; 699} 700 701void FontRenderer::finishRender() { 702 mBounds = nullptr; 703 mClip = nullptr; 704 705 issueDrawCommand(); 706} 707 708void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, 709 const SkMatrix& matrix) { 710 Font* font = Font::create(this, paint, matrix); 711 font->precache(paint, text, numGlyphs); 712} 713 714void FontRenderer::endPrecaching() { 715 checkTextureUpdate(); 716} 717 718bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, 719 uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, 720 const float* positions, Rect* bounds, TextSetupFunctor* functor, bool forceFinish) { 721 if (!mCurrentFont) { 722 ALOGE("No font set"); 723 return false; 724 } 725 726 initRender(clip, bounds, functor); 727 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); 728 729 if (forceFinish) { 730 finishRender(); 731 } 732 733 return mDrawn; 734} 735 736bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, 737 uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, 738 float hOffset, float vOffset, Rect* bounds, TextSetupFunctor* functor) { 739 if (!mCurrentFont) { 740 ALOGE("No font set"); 741 return false; 742 } 743 744 initRender(clip, bounds, functor); 745 mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); 746 finishRender(); 747 748 return mDrawn; 749} 750 751void FontRenderer::removeFont(const Font* font) { 752 mActiveFonts.remove(font->getDescription()); 753 754 if (mCurrentFont == font) { 755 mCurrentFont = nullptr; 756 } 757} 758 759void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { 760 uint32_t intRadius = Blur::convertRadiusToInt(radius); 761#ifdef ANDROID_ENABLE_RENDERSCRIPT 762 if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { 763 uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); 764 765 if (mRs == nullptr) { 766 mRs = new RSC::RS(); 767 // a null path is OK because there are no custom kernels used 768 // hence nothing gets cached by RS 769 if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { 770 mRs.clear(); 771 ALOGE("blur RS failed to init"); 772 } else { 773 mRsElement = RSC::Element::A_8(mRs); 774 mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); 775 } 776 } 777 if (mRs != nullptr) { 778 RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); 779 RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, 780 RS_ALLOCATION_MIPMAP_NONE, 781 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 782 *image); 783 RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, 784 RS_ALLOCATION_MIPMAP_NONE, 785 RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, 786 outImage); 787 788 mRsScript->setRadius(radius); 789 mRsScript->setInput(ain); 790 mRsScript->forEach(aout); 791 792 // replace the original image's pointer, avoiding a copy back to the original buffer 793 free(*image); 794 *image = outImage; 795 796 return; 797 } 798 } 799#endif 800 801 std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); 802 Blur::generateGaussianWeights(gaussian.get(), intRadius); 803 804 std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); 805 Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); 806 Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); 807} 808 809static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) { 810 uint32_t size = 0; 811 for (uint32_t i = 0; i < cacheTextures.size(); i++) { 812 CacheTexture* cacheTexture = cacheTextures[i]; 813 if (cacheTexture && cacheTexture->getPixelBuffer()) { 814 size += cacheTexture->getPixelBuffer()->getSize(); 815 } 816 } 817 return size; 818} 819 820uint32_t FontRenderer::getCacheSize(GLenum format) const { 821 switch (format) { 822 case GL_ALPHA: { 823 return calculateCacheSize(mACacheTextures); 824 } 825 case GL_RGBA: { 826 return calculateCacheSize(mRGBACacheTextures); 827 } 828 default: { 829 return 0; 830 } 831 } 832} 833 834}; // namespace uirenderer 835}; // namespace android 836