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