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