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