GradientCache.cpp revision ca89e2a68703bd428e8b66547d033a6ed35b3595
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 26namespace android { 27namespace uirenderer { 28 29/////////////////////////////////////////////////////////////////////////////// 30// Defines 31/////////////////////////////////////////////////////////////////////////////// 32 33#define GRADIENT_TEXTURE_HEIGHT 2 34#define GRADIENT_BYTES_PER_PIXEL 4 35 36/////////////////////////////////////////////////////////////////////////////// 37// Functions 38/////////////////////////////////////////////////////////////////////////////// 39 40template<typename T> 41static inline T min(T a, T b) { 42 return a < b ? a : b; 43} 44 45/////////////////////////////////////////////////////////////////////////////// 46// Cache entry 47/////////////////////////////////////////////////////////////////////////////// 48 49hash_t GradientCacheEntry::hash() const { 50 uint32_t hash = JenkinsHashMix(0, count); 51 for (uint32_t i = 0; i < count; i++) { 52 hash = JenkinsHashMix(hash, android::hash_type(colors[i])); 53 hash = JenkinsHashMix(hash, android::hash_type(positions[i])); 54 } 55 return JenkinsHashWhiten(hash); 56} 57 58int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) { 59 int deltaInt = int(lhs.count) - int(rhs.count); 60 if (deltaInt != 0) return deltaInt; 61 62 deltaInt = memcmp(lhs.colors, rhs.colors, lhs.count * sizeof(uint32_t)); 63 if (deltaInt != 0) return deltaInt; 64 65 return memcmp(lhs.positions, rhs.positions, lhs.count * sizeof(float)); 66} 67 68/////////////////////////////////////////////////////////////////////////////// 69// Constructors/destructor 70/////////////////////////////////////////////////////////////////////////////// 71 72GradientCache::GradientCache(): 73 mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity), 74 mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) { 75 char property[PROPERTY_VALUE_MAX]; 76 if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) { 77 INIT_LOGD(" Setting gradient cache size to %sMB", property); 78 setMaxSize(MB(atof(property))); 79 } else { 80 INIT_LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); 81 } 82 83 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); 84 85 mCache.setOnEntryRemovedListener(this); 86} 87 88GradientCache::GradientCache(uint32_t maxByteSize): 89 mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity), 90 mSize(0), mMaxSize(maxByteSize) { 91 mCache.setOnEntryRemovedListener(this); 92} 93 94GradientCache::~GradientCache() { 95 mCache.clear(); 96} 97 98/////////////////////////////////////////////////////////////////////////////// 99// Size management 100/////////////////////////////////////////////////////////////////////////////// 101 102uint32_t GradientCache::getSize() { 103 return mSize; 104} 105 106uint32_t GradientCache::getMaxSize() { 107 return mMaxSize; 108} 109 110void GradientCache::setMaxSize(uint32_t maxSize) { 111 mMaxSize = maxSize; 112 while (mSize > mMaxSize) { 113 mCache.removeOldest(); 114 } 115} 116 117/////////////////////////////////////////////////////////////////////////////// 118// Callbacks 119/////////////////////////////////////////////////////////////////////////////// 120 121void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) { 122 if (texture) { 123 const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL; 124 mSize -= size; 125 126 glDeleteTextures(1, &texture->id); 127 delete texture; 128 } 129} 130 131/////////////////////////////////////////////////////////////////////////////// 132// Caching 133/////////////////////////////////////////////////////////////////////////////// 134 135Texture* GradientCache::get(uint32_t* colors, float* positions, int count) { 136 GradientCacheEntry gradient(colors, positions, count); 137 Texture* texture = mCache.get(gradient); 138 139 if (!texture) { 140 texture = addLinearGradient(gradient, colors, positions, count); 141 } 142 143 return texture; 144} 145 146void GradientCache::clear() { 147 mCache.clear(); 148} 149 150void GradientCache::getGradientInfo(const uint32_t* colors, const int count, 151 GradientInfo& info) { 152 uint32_t width = 256 * (count - 1); 153 154 if (!Extensions::getInstance().hasNPot()) { 155 width = 1 << (31 - __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 170Texture* 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 = GRADIENT_TEXTURE_HEIGHT; 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 * GRADIENT_BYTES_PER_PIXEL; 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 196void GradientCache::generateTexture(uint32_t* colors, float* positions, 197 int count, Texture* texture) { 198 199 const uint32_t width = texture->width; 200 const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL; 201 uint32_t pixels[width * texture->height]; 202 203 int currentPos = 1; 204 205 float startA = (colors[0] >> 24) & 0xff; 206 float startR = (colors[0] >> 16) & 0xff; 207 float startG = (colors[0] >> 8) & 0xff; 208 float startB = (colors[0] >> 0) & 0xff; 209 210 float endA = (colors[1] >> 24) & 0xff; 211 float endR = (colors[1] >> 16) & 0xff; 212 float endG = (colors[1] >> 8) & 0xff; 213 float endB = (colors[1] >> 0) & 0xff; 214 215 float start = positions[0]; 216 float distance = positions[1] - start; 217 218 uint8_t* p = (uint8_t*) pixels; 219 for (uint32_t x = 0; x < width; x++) { 220 float pos = x / float(width - 1); 221 if (pos > positions[currentPos]) { 222 startA = endA; 223 startR = endR; 224 startG = endG; 225 startB = endB; 226 start = positions[currentPos]; 227 228 currentPos++; 229 230 endA = (colors[currentPos] >> 24) & 0xff; 231 endR = (colors[currentPos] >> 16) & 0xff; 232 endG = (colors[currentPos] >> 8) & 0xff; 233 endB = (colors[currentPos] >> 0) & 0xff; 234 distance = positions[currentPos] - start; 235 } 236 237 float amount = (pos - start) / distance; 238 float oppAmount = 1.0f - amount; 239 240 const float alpha = startA * oppAmount + endA * amount; 241 const float a = alpha / 255.0f; 242 *p++ = uint8_t(a * (startR * oppAmount + endR * amount)); 243 *p++ = uint8_t(a * (startG * oppAmount + endG * amount)); 244 *p++ = uint8_t(a * (startB * oppAmount + endB * amount)); 245 *p++ = uint8_t(alpha); 246 } 247 248 for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) { 249 memcpy(pixels + width * i, pixels, rowBytes); 250 } 251 252 glGenTextures(1, &texture->id); 253 254 glBindTexture(GL_TEXTURE_2D, texture->id); 255 glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL); 256 257 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0, 258 GL_RGBA, GL_UNSIGNED_BYTE, pixels); 259 260 texture->setFilter(GL_LINEAR); 261 texture->setWrap(GL_CLAMP_TO_EDGE); 262} 263 264}; // namespace uirenderer 265}; // namespace android 266