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 
luminance(const SkPaint * paint)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 
createRenderer()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 
GammaFontRenderer()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 
~GammaFontRenderer()90 GammaFontRenderer::~GammaFontRenderer() {
91 }
92 
93 ///////////////////////////////////////////////////////////////////////////////
94 // Shader-based renderer
95 ///////////////////////////////////////////////////////////////////////////////
96 
ShaderGammaFontRenderer(bool multiGamma)97 ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma)
98         : GammaFontRenderer() {
99     INIT_LOGD("Creating shader gamma font renderer");
100     mRenderer = nullptr;
101     mMultiGamma = multiGamma;
102 }
103 
describe(ProgramDescription & description,const SkPaint * paint) const104 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 
setupProgram(ProgramDescription & description,Program & program) const124 void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
125         Program& program) const {
126     if (description.hasGammaCorrection) {
127         glUniform1f(program.getUniform("gamma"), description.gamma);
128     }
129 }
130 
endPrecaching()131 void ShaderGammaFontRenderer::endPrecaching() {
132     if (mRenderer) {
133         mRenderer->endPrecaching();
134     }
135 }
136 
137 ///////////////////////////////////////////////////////////////////////////////
138 // Lookup-based renderer
139 ///////////////////////////////////////////////////////////////////////////////
140 
LookupGammaFontRenderer()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 
endPrecaching()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 
Lookup3GammaFontRenderer()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 
endPrecaching()187 void Lookup3GammaFontRenderer::endPrecaching() {
188     for (int i = 0; i < kGammaCount; i++) {
189         if (mRenderers[i]) {
190             mRenderers[i]->endPrecaching();
191         }
192     }
193 }
194 
clear()195 void Lookup3GammaFontRenderer::clear() {
196     for (int i = 0; i < kGammaCount; i++) {
197         mRenderers[i].release();
198     }
199 }
200 
flush()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 
getRenderer(Gamma gamma)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 
getFontRenderer(const SkPaint * paint)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