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