• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <utils/JenkinsHash.h>
20 
21 #include "Caches.h"
22 #include "Debug.h"
23 #include "GradientCache.h"
24 #include "Properties.h"
25 
26 namespace android {
27 namespace uirenderer {
28 
29 ///////////////////////////////////////////////////////////////////////////////
30 // Functions
31 ///////////////////////////////////////////////////////////////////////////////
32 
33 template<typename T>
min(T a,T b)34 static inline T min(T a, T b) {
35     return a < b ? a : b;
36 }
37 
38 ///////////////////////////////////////////////////////////////////////////////
39 // Cache entry
40 ///////////////////////////////////////////////////////////////////////////////
41 
hash() const42 hash_t GradientCacheEntry::hash() const {
43     uint32_t hash = JenkinsHashMix(0, count);
44     for (uint32_t i = 0; i < count; i++) {
45         hash = JenkinsHashMix(hash, android::hash_type(colors[i]));
46         hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
47     }
48     return JenkinsHashWhiten(hash);
49 }
50 
compare(const GradientCacheEntry & lhs,const GradientCacheEntry & rhs)51 int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
52     int deltaInt = int(lhs.count) - int(rhs.count);
53     if (deltaInt != 0) return deltaInt;
54 
55     deltaInt = memcmp(lhs.colors.get(), rhs.colors.get(), lhs.count * sizeof(uint32_t));
56     if (deltaInt != 0) return deltaInt;
57 
58     return memcmp(lhs.positions.get(), rhs.positions.get(), lhs.count * sizeof(float));
59 }
60 
61 ///////////////////////////////////////////////////////////////////////////////
62 // Constructors/destructor
63 ///////////////////////////////////////////////////////////////////////////////
64 
GradientCache(Extensions & extensions)65 GradientCache::GradientCache(Extensions& extensions)
66         : mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity)
67         , mSize(0)
68         , mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE))
69         , mUseFloatTexture(extensions.hasFloatTextures())
70         , mHasNpot(extensions.hasNPot()){
71     char property[PROPERTY_VALUE_MAX];
72     if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, nullptr) > 0) {
73         INIT_LOGD("  Setting gradient cache size to %sMB", property);
74         setMaxSize(MB(atof(property)));
75     } else {
76         INIT_LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
77     }
78 
79     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
80 
81     mCache.setOnEntryRemovedListener(this);
82 }
83 
~GradientCache()84 GradientCache::~GradientCache() {
85     mCache.clear();
86 }
87 
88 ///////////////////////////////////////////////////////////////////////////////
89 // Size management
90 ///////////////////////////////////////////////////////////////////////////////
91 
getSize()92 uint32_t GradientCache::getSize() {
93     return mSize;
94 }
95 
getMaxSize()96 uint32_t GradientCache::getMaxSize() {
97     return mMaxSize;
98 }
99 
setMaxSize(uint32_t maxSize)100 void GradientCache::setMaxSize(uint32_t maxSize) {
101     mMaxSize = maxSize;
102     while (mSize > mMaxSize) {
103         mCache.removeOldest();
104     }
105 }
106 
107 ///////////////////////////////////////////////////////////////////////////////
108 // Callbacks
109 ///////////////////////////////////////////////////////////////////////////////
110 
operator ()(GradientCacheEntry &,Texture * & texture)111 void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) {
112     if (texture) {
113         const uint32_t size = texture->width * texture->height * bytesPerPixel();
114         mSize -= size;
115 
116         texture->deleteTexture();
117         delete texture;
118     }
119 }
120 
121 ///////////////////////////////////////////////////////////////////////////////
122 // Caching
123 ///////////////////////////////////////////////////////////////////////////////
124 
get(uint32_t * colors,float * positions,int count)125 Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
126     GradientCacheEntry gradient(colors, positions, count);
127     Texture* texture = mCache.get(gradient);
128 
129     if (!texture) {
130         texture = addLinearGradient(gradient, colors, positions, count);
131     }
132 
133     return texture;
134 }
135 
clear()136 void GradientCache::clear() {
137     mCache.clear();
138 }
139 
getGradientInfo(const uint32_t * colors,const int count,GradientInfo & info)140 void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
141         GradientInfo& info) {
142     uint32_t width = 256 * (count - 1);
143 
144     // If the npot extension is not supported we cannot use non-clamp
145     // wrap modes. We therefore find the nearest largest power of 2
146     // unless width is already a power of 2
147     if (!mHasNpot && (width & (width - 1)) != 0) {
148         width = 1 << (32 - __builtin_clz(width));
149     }
150 
151     bool hasAlpha = false;
152     for (int i = 0; i < count; i++) {
153         if (((colors[i] >> 24) & 0xff) < 255) {
154             hasAlpha = true;
155             break;
156         }
157     }
158 
159     info.width = min(width, uint32_t(mMaxTextureSize));
160     info.hasAlpha = hasAlpha;
161 }
162 
addLinearGradient(GradientCacheEntry & gradient,uint32_t * colors,float * positions,int count)163 Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
164         uint32_t* colors, float* positions, int count) {
165 
166     GradientInfo info;
167     getGradientInfo(colors, count, info);
168 
169     Texture* texture = new Texture(Caches::getInstance());
170     texture->width = info.width;
171     texture->height = 2;
172     texture->blend = info.hasAlpha;
173     texture->generation = 1;
174 
175     // Asume the cache is always big enough
176     const uint32_t size = texture->width * texture->height * bytesPerPixel();
177     while (getSize() + size > mMaxSize) {
178         mCache.removeOldest();
179     }
180 
181     generateTexture(colors, positions, texture);
182 
183     mSize += size;
184     mCache.put(gradient, texture);
185 
186     return texture;
187 }
188 
bytesPerPixel() const189 size_t GradientCache::bytesPerPixel() const {
190     // We use 4 channels (RGBA)
191     return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
192 }
193 
splitToBytes(uint32_t inColor,GradientColor & outColor) const194 void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
195     outColor.r = (inColor >> 16) & 0xff;
196     outColor.g = (inColor >>  8) & 0xff;
197     outColor.b = (inColor >>  0) & 0xff;
198     outColor.a = (inColor >> 24) & 0xff;
199 }
200 
splitToFloats(uint32_t inColor,GradientColor & outColor) const201 void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
202     outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
203     outColor.g = ((inColor >>  8) & 0xff) / 255.0f;
204     outColor.b = ((inColor >>  0) & 0xff) / 255.0f;
205     outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
206 }
207 
mixBytes(GradientColor & start,GradientColor & end,float amount,uint8_t * & dst) const208 void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
209         uint8_t*& dst) const {
210     float oppAmount = 1.0f - amount;
211     const float alpha = start.a * oppAmount + end.a * amount;
212     const float a = alpha / 255.0f;
213 
214     *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
215     *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
216     *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
217     *dst++ = uint8_t(alpha);
218 }
219 
mixFloats(GradientColor & start,GradientColor & end,float amount,uint8_t * & dst) const220 void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
221         uint8_t*& dst) const {
222     float oppAmount = 1.0f - amount;
223     const float a = start.a * oppAmount + end.a * amount;
224 
225     float* d = (float*) dst;
226     *d++ = a * (start.r * oppAmount + end.r * amount);
227     *d++ = a * (start.g * oppAmount + end.g * amount);
228     *d++ = a * (start.b * oppAmount + end.b * amount);
229     *d++ = a;
230 
231     dst += 4 * sizeof(float);
232 }
233 
generateTexture(uint32_t * colors,float * positions,Texture * texture)234 void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) {
235     const uint32_t width = texture->width;
236     const GLsizei rowBytes = width * bytesPerPixel();
237     uint8_t pixels[rowBytes * texture->height];
238 
239     static ChannelSplitter gSplitters[] = {
240             &android::uirenderer::GradientCache::splitToBytes,
241             &android::uirenderer::GradientCache::splitToFloats,
242     };
243     ChannelSplitter split = gSplitters[mUseFloatTexture];
244 
245     static ChannelMixer gMixers[] = {
246             &android::uirenderer::GradientCache::mixBytes,
247             &android::uirenderer::GradientCache::mixFloats,
248     };
249     ChannelMixer mix = gMixers[mUseFloatTexture];
250 
251     GradientColor start;
252     (this->*split)(colors[0], start);
253 
254     GradientColor end;
255     (this->*split)(colors[1], end);
256 
257     int currentPos = 1;
258     float startPos = positions[0];
259     float distance = positions[1] - startPos;
260 
261     uint8_t* dst = pixels;
262     for (uint32_t x = 0; x < width; x++) {
263         float pos = x / float(width - 1);
264         if (pos > positions[currentPos]) {
265             start = end;
266             startPos = positions[currentPos];
267 
268             currentPos++;
269 
270             (this->*split)(colors[currentPos], end);
271             distance = positions[currentPos] - startPos;
272         }
273 
274         float amount = (pos - startPos) / distance;
275         (this->*mix)(start, end, amount, dst);
276     }
277 
278     memcpy(pixels + rowBytes, pixels, rowBytes);
279 
280     glGenTextures(1, &texture->id);
281     Caches::getInstance().textureState().bindTexture(texture->id);
282     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
283 
284     if (mUseFloatTexture) {
285         // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
286         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
287                 GL_RGBA, GL_FLOAT, pixels);
288     } else {
289         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
290                 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
291     }
292 
293     texture->setFilter(GL_LINEAR);
294     texture->setWrap(GL_CLAMP_TO_EDGE);
295 }
296 
297 }; // namespace uirenderer
298 }; // namespace android
299