diff options
Diffstat (limited to 'libs/hwui/FontRenderer.cpp')
-rw-r--r-- | libs/hwui/FontRenderer.cpp | 802 |
1 files changed, 0 insertions, 802 deletions
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp deleted file mode 100644 index bbcedb15335d..000000000000 --- a/libs/hwui/FontRenderer.cpp +++ /dev/null @@ -1,802 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "FontRenderer.h" - -#include "BakedOpDispatcher.h" -#include "BakedOpRenderer.h" -#include "BakedOpState.h" -#include "Caches.h" -#include "Debug.h" -#include "Extensions.h" -#include "Glop.h" -#include "GlopBuilder.h" -#include "PixelBuffer.h" -#include "Rect.h" -#include "font/Font.h" -#include "renderstate/RenderState.h" -#include "utils/Blur.h" -#include "utils/Timing.h" - -#include <RenderScript.h> -#include <SkGlyph.h> -#include <SkUtils.h> -#include <utils/Log.h> -#include <algorithm> - -namespace android { -namespace uirenderer { - -// blur inputs smaller than this constant will bypass renderscript -#define RS_MIN_INPUT_CUTOFF 10000 - -/////////////////////////////////////////////////////////////////////////////// -// TextSetupFunctor -/////////////////////////////////////////////////////////////////////////////// - -void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { - int textureFillFlags = TextureFillFlags::None; - if (texture.getFormat() == GL_ALPHA) { - textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture; - } - if (linearFiltering) { - textureFillFlags |= TextureFillFlags::ForceFilter; - } - int transformFlags = - pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; -#ifdef ANDROID_ENABLE_LINEAR_BLENDING - bool gammaCorrection = true; -#else - bool gammaCorrection = false; -#endif - Glop glop; - GlopBuilder(renderer->renderState(), renderer->caches(), &glop) - .setRoundRectClipState(bakedState->roundRectClipState) - .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) - .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) - .setGammaCorrection(gammaCorrection) - .setTransform(bakedState->computedState.transform, transformFlags) - .setModelViewIdentityEmptyBounds() - .build(); - // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer - renderer->renderGlop(nullptr, clip, glop); -} - -/////////////////////////////////////////////////////////////////////////////// -// FontRenderer -/////////////////////////////////////////////////////////////////////////////// - -static bool sLogFontRendererCreate = true; - -FontRenderer::FontRenderer(const uint8_t* gammaTable) - : mGammaTable(gammaTable) - , mCurrentFont(nullptr) - , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) - , mCurrentCacheTexture(nullptr) - , mUploadTexture(false) - , mFunctor(nullptr) - , mClip(nullptr) - , mBounds(nullptr) - , mDrawn(false) - , mInitialized(false) - , mLinearFiltering(false) { - if (sLogFontRendererCreate) { - INIT_LOGD("Creating FontRenderer"); - } - - auto deviceInfo = DeviceInfo::get(); - auto displayInfo = deviceInfo->displayInfo(); - int maxTextureSize = deviceInfo->maxTextureSize(); - - // Adjust cache size based on Pixel's desnsity. - constexpr float PIXEL_DENSITY = 2.6; - const float densityRatio = displayInfo.density / PIXEL_DENSITY; - - // TODO: Most devices are hardcoded with this configuration, does it need to be dynamic? - mSmallCacheWidth = - OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio); - mSmallCacheHeight = - OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio); - mLargeCacheWidth = - OffscreenBuffer::computeIdealDimension(std::min(2048, maxTextureSize) * densityRatio); - mLargeCacheHeight = - OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio); - - if (sLogFontRendererCreate) { - INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i", - mSmallCacheWidth, mSmallCacheHeight, mLargeCacheWidth, mLargeCacheHeight >> 1, - mLargeCacheWidth, mLargeCacheHeight >> 1, mLargeCacheWidth, mLargeCacheHeight); - } - - sLogFontRendererCreate = false; -} - -void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) { - for (uint32_t i = 0; i < cacheTextures.size(); i++) { - delete cacheTextures[i]; - } - cacheTextures.clear(); -} - -FontRenderer::~FontRenderer() { - clearCacheTextures(mACacheTextures); - clearCacheTextures(mRGBACacheTextures); - - LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); - while (it.next()) { - delete it.value(); - } - mActiveFonts.clear(); -} - -void FontRenderer::flushAllAndInvalidate() { - issueDrawCommand(); - - LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); - while (it.next()) { - it.value()->invalidateTextureCache(); - } - - for (uint32_t i = 0; i < mACacheTextures.size(); i++) { - mACacheTextures[i]->init(); - -#ifdef BUGREPORT_FONT_CACHE_USAGE - mHistoryTracker.glyphsCleared(mACacheTextures[i]); -#endif - } - - for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { - mRGBACacheTextures[i]->init(); -#ifdef BUGREPORT_FONT_CACHE_USAGE - mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]); -#endif - } - - mDrawn = false; -} - -void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) { - // Start from 1; don't deallocate smallest/default texture - for (uint32_t i = 1; i < cacheTextures.size(); i++) { - CacheTexture* cacheTexture = cacheTextures[i]; - if (cacheTexture->getPixelBuffer()) { - cacheTexture->init(); -#ifdef BUGREPORT_FONT_CACHE_USAGE - mHistoryTracker.glyphsCleared(cacheTexture); -#endif - LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); - while (it.next()) { - it.value()->invalidateTextureCache(cacheTexture); - } - cacheTexture->releasePixelBuffer(); - } - } -} - -void FontRenderer::flushLargeCaches() { - flushLargeCaches(mACacheTextures); - flushLargeCaches(mRGBACacheTextures); -} - -CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures, - const SkGlyph& glyph, uint32_t* startX, - uint32_t* startY) { - for (uint32_t i = 0; i < cacheTextures.size(); i++) { - if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) { - return cacheTextures[i]; - } - } - // Could not fit glyph into current cache textures - return nullptr; -} - -void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, - uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { - checkInit(); - - // If the glyph bitmap is empty let's assum the glyph is valid - // so we can avoid doing extra work later on - if (glyph.fWidth == 0 || glyph.fHeight == 0) { - cachedGlyph->mIsValid = true; - cachedGlyph->mCacheTexture = nullptr; - return; - } - - cachedGlyph->mIsValid = false; - - // choose an appropriate cache texture list for this glyph format - SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat); - std::vector<CacheTexture*>* cacheTextures = nullptr; - switch (format) { - case SkMask::kA8_Format: - case SkMask::kBW_Format: - cacheTextures = &mACacheTextures; - break; - case SkMask::kARGB32_Format: - cacheTextures = &mRGBACacheTextures; - break; - default: -#if DEBUG_FONT_RENDERER - ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format); -#endif - return; - } - - // If the glyph is too tall, don't cache it - if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > - (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) { - ALOGE("Font size too large to fit in cache. width, height = %i, %i", (int)glyph.fWidth, - (int)glyph.fHeight); - return; - } - - // Now copy the bitmap into the cache texture - uint32_t startX = 0; - uint32_t startY = 0; - - CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY); - - if (!cacheTexture) { - if (!precaching) { - // If the new glyph didn't fit and we are not just trying to precache it, - // clear out the cache and try again - flushAllAndInvalidate(); - cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY); - } - - if (!cacheTexture) { - // either the glyph didn't fit or we're precaching and will cache it when we draw - return; - } - } - - cachedGlyph->mCacheTexture = cacheTexture; - - *retOriginX = startX; - *retOriginY = startY; - - uint32_t endX = startX + glyph.fWidth; - uint32_t endY = startY + glyph.fHeight; - - uint32_t cacheWidth = cacheTexture->getWidth(); - - if (!cacheTexture->getPixelBuffer()) { - Caches::getInstance().textureState().activateTexture(0); - // Large-glyph texture memory is allocated only as needed - cacheTexture->allocatePixelBuffer(); - } - if (!cacheTexture->mesh()) { - cacheTexture->allocateMesh(); - } - - uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map(); - uint8_t* bitmapBuffer = (uint8_t*)glyph.fImage; - int srcStride = glyph.rowBytes(); - - // Copy the glyph image, taking the mask format into account - switch (format) { - case SkMask::kA8_Format: { - uint32_t row = - (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE; - // write leading border line - memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); - // write glyph data - if (mGammaTable) { - for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { - row = cacheY * cacheWidth; - cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; - for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { - uint8_t tempCol = bitmapBuffer[bY + bX]; - cacheBuffer[row + cacheX] = mGammaTable[tempCol]; - } - cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; - } - } else { - for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) { - row = cacheY * cacheWidth; - memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth); - cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; - cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; - } - } - // write trailing border line - row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; - memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); - break; - } - case SkMask::kARGB32_Format: { - // prep data lengths - const size_t formatSize = PixelBuffer::formatSize(GL_RGBA); - const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE; - size_t rowSize = formatSize * glyph.fWidth; - // prep advances - size_t dstStride = formatSize * cacheWidth; - // prep indices - // - we actually start one row early, and then increment before first copy - uint8_t* src = &bitmapBuffer[0 - srcStride]; - uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)]; - uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)]; - uint8_t* dstL = dst - borderSize; - uint8_t* dstR = dst + rowSize; - // write leading border line - memset(dstL, 0, rowSize + 2 * borderSize); - // write glyph data - while (dst < dstEnd) { - memset(dstL += dstStride, 0, borderSize); // leading border column - memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data - memset(dstR += dstStride, 0, borderSize); // trailing border column - } - // write trailing border line - memset(dstL += dstStride, 0, rowSize + 2 * borderSize); - break; - } - case SkMask::kBW_Format: { - uint32_t cacheX = 0, cacheY = 0; - uint32_t row = - (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE; - static const uint8_t COLORS[2] = {0, 255}; - // write leading border line - memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); - // write glyph data - for (cacheY = startY; cacheY < endY; cacheY++) { - cacheX = startX; - int rowBytes = srcStride; - uint8_t* buffer = bitmapBuffer; - - row = cacheY * cacheWidth; - cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0; - while (--rowBytes >= 0) { - uint8_t b = *buffer++; - for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) { - cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1]; - } - } - cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0; - - bitmapBuffer += srcStride; - } - // write trailing border line - row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE; - memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE); - break; - } - default: - ALOGW("Unknown glyph format: 0x%x", format); - break; - } - - cachedGlyph->mIsValid = true; - -#ifdef BUGREPORT_FONT_CACHE_USAGE - mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight); -#endif -} - -CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, - bool allocate) { - CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads); - - if (allocate) { - Caches::getInstance().textureState().activateTexture(0); - cacheTexture->allocatePixelBuffer(); - cacheTexture->allocateMesh(); - } - - return cacheTexture; -} - -void FontRenderer::initTextTexture() { - clearCacheTextures(mACacheTextures); - clearCacheTextures(mRGBACacheTextures); - - mUploadTexture = false; - mACacheTextures.push_back( - createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_ALPHA, true)); - mACacheTextures.push_back( - createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false)); - mACacheTextures.push_back( - createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false)); - mACacheTextures.push_back( - createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, GL_ALPHA, false)); - mRGBACacheTextures.push_back( - createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_RGBA, false)); - mRGBACacheTextures.push_back( - createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_RGBA, false)); - mCurrentCacheTexture = mACacheTextures[0]; -} - -// We don't want to allocate anything unless we actually draw text -void FontRenderer::checkInit() { - if (mInitialized) { - return; - } - - initTextTexture(); - - mInitialized = true; -} - -void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures, - bool& resetPixelStore, GLuint& lastTextureId) { - for (uint32_t i = 0; i < cacheTextures.size(); i++) { - CacheTexture* cacheTexture = cacheTextures[i]; - if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) { - if (cacheTexture->getTextureId() != lastTextureId) { - lastTextureId = cacheTexture->getTextureId(); - caches.textureState().activateTexture(0); - caches.textureState().bindTexture(lastTextureId); - } - - if (cacheTexture->upload()) { - resetPixelStore = true; - } - } - } -} - -void FontRenderer::checkTextureUpdate() { - if (!mUploadTexture) { - return; - } - - Caches& caches = Caches::getInstance(); - GLuint lastTextureId = 0; - - bool resetPixelStore = false; - - // Iterate over all the cache textures and see which ones need to be updated - checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId); - checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId); - - // Unbind any PBO we might have used to update textures - caches.pixelBufferState().unbind(); - - // Reset to default unpack row length to avoid affecting texture - // uploads in other parts of the renderer - if (resetPixelStore) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - - mUploadTexture = false; -} - -void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) { - if (!mFunctor) return; - - bool first = true; - for (uint32_t i = 0; i < cacheTextures.size(); i++) { - CacheTexture* texture = cacheTextures[i]; - if (texture->canDraw()) { - if (first) { - checkTextureUpdate(); - first = false; - mDrawn = true; - } - - mFunctor->draw(*texture, mLinearFiltering); - - texture->resetMesh(); - } - } -} - -void FontRenderer::issueDrawCommand() { - issueDrawCommand(mACacheTextures); - issueDrawCommand(mRGBACacheTextures); -} - -void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, - float u2, float v2, float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, - CacheTexture* texture) { - if (texture != mCurrentCacheTexture) { - // Now use the new texture id - mCurrentCacheTexture = texture; - } - - mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4); -} - -void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, - float u2, float v2, float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, CacheTexture* texture) { - if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { - return; - } - - appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); - - if (mBounds) { - mBounds->left = std::min(mBounds->left, x1); - mBounds->top = std::min(mBounds->top, y3); - mBounds->right = std::max(mBounds->right, x3); - mBounds->bottom = std::max(mBounds->bottom, y1); - } - - if (mCurrentCacheTexture->endOfMesh()) { - issueDrawCommand(); - } -} - -void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, - float u2, float v2, float x3, float y3, float u3, float v3, - float x4, float y4, float u4, float v4, - CacheTexture* texture) { - appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); - - if (mBounds) { - mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4)))); - mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4)))); - mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4)))); - mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4)))); - } - - if (mCurrentCacheTexture->endOfMesh()) { - issueDrawCommand(); - } -} - -void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { - mCurrentFont = Font::create(this, paint, matrix); -} - -FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t* glyphs, - int numGlyphs, float radius, - const float* positions) { - checkInit(); - - DropShadow image; - image.width = 0; - image.height = 0; - image.image = nullptr; - image.penX = 0; - image.penY = 0; - - if (!mCurrentFont) { - return image; - } - - mDrawn = false; - mClip = nullptr; - mBounds = nullptr; - - Rect bounds; - mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions); - - uint32_t intRadius = Blur::convertRadiusToInt(radius); - uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2 * intRadius; - uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2 * intRadius; - - uint32_t maxSize = Caches::getInstance().maxTextureSize; - if (paddedWidth > maxSize || paddedHeight > maxSize) { - return image; - } - - // Align buffers for renderscript usage - if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { - paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; - } - int size = paddedWidth * paddedHeight; - uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); - - memset(dataBuffer, 0, size); - - int penX = intRadius - bounds.left; - int penY = intRadius - bounds.bottom; - - if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { - // text has non-whitespace, so draw and blur to create the shadow - // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted - // TODO: don't draw pure whitespace in the first place, and avoid needing this check - mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY, Font::BITMAP, dataBuffer, - paddedWidth, paddedHeight, nullptr, positions); - - // Unbind any PBO we might have used - Caches::getInstance().pixelBufferState().unbind(); - - blurImage(&dataBuffer, paddedWidth, paddedHeight, radius); - } - - image.width = paddedWidth; - image.height = paddedHeight; - image.image = dataBuffer; - image.penX = penX; - image.penY = penY; - - return image; -} - -void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) { - checkInit(); - - mDrawn = false; - mBounds = bounds; - mFunctor = functor; - mClip = clip; -} - -void FontRenderer::finishRender() { - mBounds = nullptr; - mClip = nullptr; - - issueDrawCommand(); -} - -void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, - const SkMatrix& matrix) { - Font* font = Font::create(this, paint, matrix); - font->precache(paint, glyphs, numGlyphs); -} - -void FontRenderer::endPrecaching() { - checkTextureUpdate(); -} - -bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, - int numGlyphs, int x, int y, const float* positions, Rect* bounds, - TextDrawFunctor* functor, bool forceFinish) { - if (!mCurrentFont) { - ALOGE("No font set"); - return false; - } - - initRender(clip, bounds, functor); - mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions); - - if (forceFinish) { - finishRender(); - } - - return mDrawn; -} - -bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, - int numGlyphs, const SkPath* path, float hOffset, float vOffset, - Rect* bounds, TextDrawFunctor* functor) { - if (!mCurrentFont) { - ALOGE("No font set"); - return false; - } - - initRender(clip, bounds, functor); - mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset); - finishRender(); - - return mDrawn; -} - -void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { - uint32_t intRadius = Blur::convertRadiusToInt(radius); - if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) { - uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); - - if (mRs == nullptr) { - mRs = new RSC::RS(); - // a null path is OK because there are no custom kernels used - // hence nothing gets cached by RS - if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { - mRs.clear(); - ALOGE("blur RS failed to init"); - } else { - mRsElement = RSC::Element::A_8(mRs); - mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement); - } - } - if (mRs != nullptr) { - RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); - RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped( - mRs, t, RS_ALLOCATION_MIPMAP_NONE, - RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); - RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped( - mRs, t, RS_ALLOCATION_MIPMAP_NONE, - RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); - - mRsScript->setRadius(radius); - mRsScript->setInput(ain); - mRsScript->forEach(aout); - - // replace the original image's pointer, avoiding a copy back to the original buffer - free(*image); - *image = outImage; - - return; - } - } - - std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]); - Blur::generateGaussianWeights(gaussian.get(), radius); - - std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]); - Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height); - Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height); -} - -static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) { - uint32_t size = 0; - for (uint32_t i = 0; i < cacheTextures.size(); i++) { - CacheTexture* cacheTexture = cacheTextures[i]; - if (cacheTexture && cacheTexture->getPixelBuffer()) { - size += cacheTexture->getPixelBuffer()->getSize(); - } - } - return size; -} - -static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) { - uint32_t size = 0; - for (uint32_t i = 0; i < cacheTextures.size(); i++) { - CacheTexture* cacheTexture = cacheTextures[i]; - if (cacheTexture && cacheTexture->getPixelBuffer()) { - size += cacheTexture->calculateFreeMemory(); - } - } - return size; -} - -const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const { - switch (format) { - case GL_ALPHA: { - return mACacheTextures; - } - case GL_RGBA: { - return mRGBACacheTextures; - } - default: { - LOG_ALWAYS_FATAL("Unsupported format: %d", format); - // Impossible to hit this, but the compiler doesn't know that - return *(new std::vector<CacheTexture*>()); - } - } -} - -static void dumpTextures(String8& log, const char* tag, - const std::vector<CacheTexture*>& cacheTextures) { - for (uint32_t i = 0; i < cacheTextures.size(); i++) { - CacheTexture* cacheTexture = cacheTextures[i]; - if (cacheTexture && cacheTexture->getPixelBuffer()) { - uint32_t free = cacheTexture->calculateFreeMemory(); - uint32_t total = cacheTexture->getPixelBuffer()->getSize(); - log.appendFormat(" %-4s texture %d %8d / %8d\n", tag, i, total - free, total); - } - } -} - -void FontRenderer::dumpMemoryUsage(String8& log) const { - const uint32_t sizeA8 = getCacheSize(GL_ALPHA); - const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA); - const uint32_t sizeRGBA = getCacheSize(GL_RGBA); - const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA); - log.appendFormat(" FontRenderer A8 %8d / %8d\n", usedA8, sizeA8); - dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA)); - log.appendFormat(" FontRenderer RGBA %8d / %8d\n", usedRGBA, sizeRGBA); - dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA)); - log.appendFormat(" FontRenderer total %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA); -} - -uint32_t FontRenderer::getCacheSize(GLenum format) const { - return calculateCacheSize(cacheTexturesForFormat(format)); -} - -uint32_t FontRenderer::getFreeCacheSize(GLenum format) const { - return calculateFreeCacheSize(cacheTexturesForFormat(format)); -} - -uint32_t FontRenderer::getSize() const { - return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA); -} - -}; // namespace uirenderer -}; // namespace android |