/* * 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 "Debug.h" #include "GammaFontRenderer.h" #include "Properties.h" namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// // Utils /////////////////////////////////////////////////////////////////////////////// static int luminance(const SkPaint* paint) { uint32_t c = paint->getColor(); const int r = (c >> 16) & 0xFF; const int g = (c >> 8) & 0xFF; const int b = (c ) & 0xFF; return (r * 2 + g * 5 + b) >> 3; } /////////////////////////////////////////////////////////////////////////////// // Base class GammaFontRenderer /////////////////////////////////////////////////////////////////////////////// GammaFontRenderer* GammaFontRenderer::createRenderer() { // Choose the best renderer char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_TEXT_GAMMA_METHOD, property, DEFAULT_TEXT_GAMMA_METHOD) > 0) { if (!strcasecmp(property, "lookup")) { return new LookupGammaFontRenderer(); } else if (!strcasecmp(property, "shader")) { return new ShaderGammaFontRenderer(false); } else if (!strcasecmp(property, "shader3")) { return new ShaderGammaFontRenderer(true); } } return new Lookup3GammaFontRenderer(); } GammaFontRenderer::GammaFontRenderer() { // Get the renderer properties char property[PROPERTY_VALUE_MAX]; // Get the gamma mGamma = DEFAULT_TEXT_GAMMA; if (property_get(PROPERTY_TEXT_GAMMA, property, nullptr) > 0) { INIT_LOGD(" Setting text gamma to %s", property); mGamma = atof(property); } else { INIT_LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA); } // Get the black gamma threshold mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, nullptr) > 0) { INIT_LOGD(" Setting text black gamma threshold to %s", property); mBlackThreshold = atoi(property); } else { INIT_LOGD(" Using default text black gamma threshold of %d", DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD); } // Get the white gamma threshold mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, nullptr) > 0) { INIT_LOGD(" Setting text white gamma threshold to %s", property); mWhiteThreshold = atoi(property); } else { INIT_LOGD(" Using default white black gamma threshold of %d", DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD); } } GammaFontRenderer::~GammaFontRenderer() { } /////////////////////////////////////////////////////////////////////////////// // Shader-based renderer /////////////////////////////////////////////////////////////////////////////// ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma) : GammaFontRenderer() { INIT_LOGD("Creating shader gamma font renderer"); mRenderer = nullptr; mMultiGamma = multiGamma; } void ShaderGammaFontRenderer::describe(ProgramDescription& description, const SkPaint* paint) const { if (paint->getShader() == nullptr) { if (mMultiGamma) { const int l = luminance(paint); if (l <= mBlackThreshold) { description.hasGammaCorrection = true; description.gamma = mGamma; } else if (l >= mWhiteThreshold) { description.hasGammaCorrection = true; description.gamma = 1.0f / mGamma; } } else { description.hasGammaCorrection = true; description.gamma = 1.0f / mGamma; } } } void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description, Program& program) const { if (description.hasGammaCorrection) { glUniform1f(program.getUniform("gamma"), description.gamma); } } void ShaderGammaFontRenderer::endPrecaching() { if (mRenderer) { mRenderer->endPrecaching(); } } /////////////////////////////////////////////////////////////////////////////// // Lookup-based renderer /////////////////////////////////////////////////////////////////////////////// LookupGammaFontRenderer::LookupGammaFontRenderer() : GammaFontRenderer() { INIT_LOGD("Creating lookup gamma font renderer"); // Compute the gamma tables const float gamma = 1.0f / mGamma; for (uint32_t i = 0; i <= 255; i++) { mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f)); } mRenderer = nullptr; } void LookupGammaFontRenderer::endPrecaching() { if (mRenderer) { mRenderer->endPrecaching(); } } /////////////////////////////////////////////////////////////////////////////// // Lookup-based renderer, using 3 different correction tables /////////////////////////////////////////////////////////////////////////////// Lookup3GammaFontRenderer::Lookup3GammaFontRenderer() : GammaFontRenderer() { INIT_LOGD("Creating lookup3 gamma font renderer"); // Compute the gamma tables const float blackGamma = mGamma; const float whiteGamma = 1.0f / mGamma; for (uint32_t i = 0; i <= 255; i++) { const float v = i / 255.0f; const float black = pow(v, blackGamma); const float white = pow(v, whiteGamma); mGammaTable[i] = i; mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f)); mGammaTable[512 + i] = uint8_t((float)::floor(white * 255.0f + 0.5f)); } memset(mRenderers, 0, sizeof(FontRenderer*) * kGammaCount); memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount); } void Lookup3GammaFontRenderer::endPrecaching() { for (int i = 0; i < kGammaCount; i++) { if (mRenderers[i]) { mRenderers[i]->endPrecaching(); } } } void Lookup3GammaFontRenderer::clear() { for (int i = 0; i < kGammaCount; i++) { mRenderers[i].reset(nullptr); } } void Lookup3GammaFontRenderer::flush() { int count = 0; int min = -1; uint32_t minCount = UINT_MAX; for (int i = 0; i < kGammaCount; i++) { if (mRenderers[i]) { count++; if (mRenderersUsageCount[i] < minCount) { minCount = mRenderersUsageCount[i]; min = i; } } } if (count <= 1 || min < 0) return; mRenderers[min].reset(nullptr); // Also eliminate the caches for large glyphs, as they consume significant memory for (int i = 0; i < kGammaCount; ++i) { if (mRenderers[i]) { mRenderers[i]->flushLargeCaches(); } } } FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) { if (!mRenderers[gamma]) { mRenderers[gamma].reset(new FontRenderer()); mRenderers[gamma]->setGammaTable(&mGammaTable[gamma * 256]); } mRenderersUsageCount[gamma]++; return mRenderers[gamma].get(); } FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) { if (paint->getShader() == nullptr) { const int l = luminance(paint); if (l <= mBlackThreshold) { return *getRenderer(kGammaBlack); } else if (l >= mWhiteThreshold) { return *getRenderer(kGammaWhite); } } return *getRenderer(kGammaDefault); } }; // namespace uirenderer }; // namespace android