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 "Debug.h" 18 #include "GammaFontRenderer.h" 19 #include "Properties.h" 20 21 namespace android { 22 namespace uirenderer { 23 24 /////////////////////////////////////////////////////////////////////////////// 25 // Utils 26 /////////////////////////////////////////////////////////////////////////////// 27 28 static int luminance(const SkPaint* paint) { 29 uint32_t c = paint->getColor(); 30 const int r = (c >> 16) & 0xFF; 31 const int g = (c >> 8) & 0xFF; 32 const int b = (c ) & 0xFF; 33 return (r * 2 + g * 5 + b) >> 3; 34 } 35 36 /////////////////////////////////////////////////////////////////////////////// 37 // Base class GammaFontRenderer 38 /////////////////////////////////////////////////////////////////////////////// 39 40 GammaFontRenderer* GammaFontRenderer::createRenderer() { 41 // Choose the best renderer 42 char property[PROPERTY_VALUE_MAX]; 43 if (property_get(PROPERTY_TEXT_GAMMA_METHOD, property, DEFAULT_TEXT_GAMMA_METHOD) > 0) { 44 if (!strcasecmp(property, "lookup")) { 45 return new LookupGammaFontRenderer(); 46 } else if (!strcasecmp(property, "shader")) { 47 return new ShaderGammaFontRenderer(false); 48 } else if (!strcasecmp(property, "shader3")) { 49 return new ShaderGammaFontRenderer(true); 50 } 51 } 52 53 return new Lookup3GammaFontRenderer(); 54 } 55 56 GammaFontRenderer::GammaFontRenderer() { 57 // Get the renderer properties 58 char property[PROPERTY_VALUE_MAX]; 59 60 // Get the gamma 61 mGamma = DEFAULT_TEXT_GAMMA; 62 if (property_get(PROPERTY_TEXT_GAMMA, property, nullptr) > 0) { 63 INIT_LOGD(" Setting text gamma to %s", property); 64 mGamma = atof(property); 65 } else { 66 INIT_LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA); 67 } 68 69 // Get the black gamma threshold 70 mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; 71 if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, nullptr) > 0) { 72 INIT_LOGD(" Setting text black gamma threshold to %s", property); 73 mBlackThreshold = atoi(property); 74 } else { 75 INIT_LOGD(" Using default text black gamma threshold of %d", 76 DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD); 77 } 78 79 // Get the white gamma threshold 80 mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; 81 if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, nullptr) > 0) { 82 INIT_LOGD(" Setting text white gamma threshold to %s", property); 83 mWhiteThreshold = atoi(property); 84 } else { 85 INIT_LOGD(" Using default white black gamma threshold of %d", 86 DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD); 87 } 88 } 89 90 GammaFontRenderer::~GammaFontRenderer() { 91 } 92 93 /////////////////////////////////////////////////////////////////////////////// 94 // Shader-based renderer 95 /////////////////////////////////////////////////////////////////////////////// 96 97 ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma) 98 : GammaFontRenderer() { 99 INIT_LOGD("Creating shader gamma font renderer"); 100 mRenderer = nullptr; 101 mMultiGamma = multiGamma; 102 } 103 104 void ShaderGammaFontRenderer::describe(ProgramDescription& description, 105 const SkPaint* paint) const { 106 if (paint->getShader() == nullptr) { 107 if (mMultiGamma) { 108 const int l = luminance(paint); 109 110 if (l <= mBlackThreshold) { 111 description.hasGammaCorrection = true; 112 description.gamma = mGamma; 113 } else if (l >= mWhiteThreshold) { 114 description.hasGammaCorrection = true; 115 description.gamma = 1.0f / mGamma; 116 } 117 } else { 118 description.hasGammaCorrection = true; 119 description.gamma = 1.0f / mGamma; 120 } 121 } 122 } 123 124 void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description, 125 Program& program) const { 126 if (description.hasGammaCorrection) { 127 glUniform1f(program.getUniform("gamma"), description.gamma); 128 } 129 } 130 131 void ShaderGammaFontRenderer::endPrecaching() { 132 if (mRenderer) { 133 mRenderer->endPrecaching(); 134 } 135 } 136 137 /////////////////////////////////////////////////////////////////////////////// 138 // Lookup-based renderer 139 /////////////////////////////////////////////////////////////////////////////// 140 141 LookupGammaFontRenderer::LookupGammaFontRenderer() 142 : GammaFontRenderer() { 143 INIT_LOGD("Creating lookup gamma font renderer"); 144 145 // Compute the gamma tables 146 const float gamma = 1.0f / mGamma; 147 148 for (uint32_t i = 0; i <= 255; i++) { 149 mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f)); 150 } 151 152 mRenderer = nullptr; 153 } 154 155 void LookupGammaFontRenderer::endPrecaching() { 156 if (mRenderer) { 157 mRenderer->endPrecaching(); 158 } 159 } 160 161 /////////////////////////////////////////////////////////////////////////////// 162 // Lookup-based renderer, using 3 different correction tables 163 /////////////////////////////////////////////////////////////////////////////// 164 165 Lookup3GammaFontRenderer::Lookup3GammaFontRenderer() 166 : GammaFontRenderer() { 167 INIT_LOGD("Creating lookup3 gamma font renderer"); 168 169 // Compute the gamma tables 170 const float blackGamma = mGamma; 171 const float whiteGamma = 1.0f / mGamma; 172 173 for (uint32_t i = 0; i <= 255; i++) { 174 const float v = i / 255.0f; 175 const float black = pow(v, blackGamma); 176 const float white = pow(v, whiteGamma); 177 178 mGammaTable[i] = i; 179 mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f)); 180 mGammaTable[512 + i] = uint8_t((float)::floor(white * 255.0f + 0.5f)); 181 } 182 183 memset(mRenderers, 0, sizeof(FontRenderer*) * kGammaCount); 184 memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount); 185 } 186 187 void Lookup3GammaFontRenderer::endPrecaching() { 188 for (int i = 0; i < kGammaCount; i++) { 189 if (mRenderers[i]) { 190 mRenderers[i]->endPrecaching(); 191 } 192 } 193 } 194 195 void Lookup3GammaFontRenderer::clear() { 196 for (int i = 0; i < kGammaCount; i++) { 197 mRenderers[i].release(); 198 } 199 } 200 201 void Lookup3GammaFontRenderer::flush() { 202 int count = 0; 203 int min = -1; 204 uint32_t minCount = UINT_MAX; 205 206 for (int i = 0; i < kGammaCount; i++) { 207 if (mRenderers[i]) { 208 count++; 209 if (mRenderersUsageCount[i] < minCount) { 210 minCount = mRenderersUsageCount[i]; 211 min = i; 212 } 213 } 214 } 215 216 if (count <= 1 || min < 0) return; 217 218 mRenderers[min].release(); 219 220 // Also eliminate the caches for large glyphs, as they consume significant memory 221 for (int i = 0; i < kGammaCount; ++i) { 222 if (mRenderers[i]) { 223 mRenderers[i]->flushLargeCaches(); 224 } 225 } 226 } 227 228 FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) { 229 if (!mRenderers[gamma]) { 230 mRenderers[gamma].reset(new FontRenderer()); 231 mRenderers[gamma]->setGammaTable(&mGammaTable[gamma * 256]); 232 } 233 mRenderersUsageCount[gamma]++; 234 return mRenderers[gamma].get(); 235 } 236 237 FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) { 238 if (paint->getShader() == nullptr) { 239 const int l = luminance(paint); 240 241 if (l <= mBlackThreshold) { 242 return *getRenderer(kGammaBlack); 243 } else if (l >= mWhiteThreshold) { 244 return *getRenderer(kGammaWhite); 245 } 246 } 247 return *getRenderer(kGammaDefault); 248 } 249 250 }; // namespace uirenderer 251 }; // namespace android 252