blob: b1c4dfee2bb2fcd0e3f82fa6e8e8d59892d81cc3 [file] [log] [blame]
Romain Guyc0ac1932010-07-19 18:43:02 -07001/*
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
Romain Guya2341a92010-09-08 18:04:33 -070019#include <utils/threads.h>
20
Romain Guyc9855a52011-01-21 21:14:15 -080021#include "Debug.h"
Romain Guyc0ac1932010-07-19 18:43:02 -070022#include "GradientCache.h"
Romain Guyfb8b7632010-08-23 21:05:08 -070023#include "Properties.h"
Romain Guyc0ac1932010-07-19 18:43:02 -070024
25namespace android {
26namespace uirenderer {
27
28///////////////////////////////////////////////////////////////////////////////
Romain Guy42e1e0d2012-07-30 14:47:51 -070029// 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///////////////////////////////////////////////////////////////////////////////
Romain Guyc0ac1932010-07-19 18:43:02 -070045// Constructors/destructor
46///////////////////////////////////////////////////////////////////////////////
47
Romain Guyfb8b7632010-08-23 21:05:08 -070048GradientCache::GradientCache():
Romain Guy6203f6c2011-08-01 18:56:21 -070049 mCache(GenerationCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
Romain Guyfb8b7632010-08-23 21:05:08 -070050 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) {
Romain Guyc9855a52011-01-21 21:14:15 -080053 INIT_LOGD(" Setting gradient cache size to %sMB", property);
Romain Guyfb8b7632010-08-23 21:05:08 -070054 setMaxSize(MB(atof(property)));
55 } else {
Romain Guyc9855a52011-01-21 21:14:15 -080056 INIT_LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
Romain Guyfb8b7632010-08-23 21:05:08 -070057 }
58
Romain Guy8dcfd5e2012-07-20 11:36:03 -070059 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
60
Romain Guyfb8b7632010-08-23 21:05:08 -070061 mCache.setOnEntryRemovedListener(this);
62}
63
Romain Guyc0ac1932010-07-19 18:43:02 -070064GradientCache::GradientCache(uint32_t maxByteSize):
Romain Guy6203f6c2011-08-01 18:56:21 -070065 mCache(GenerationCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
Romain Guyc0ac1932010-07-19 18:43:02 -070066 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
Romain Guy6203f6c2011-08-01 18:56:21 -070097void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
98 if (texture) {
Romain Guy42e1e0d2012-07-30 14:47:51 -070099 const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
Romain Guyc0ac1932010-07-19 18:43:02 -0700100 mSize -= size;
101 }
102
103 if (texture) {
104 glDeleteTextures(1, &texture->id);
105 delete texture;
106 }
107}
108
109///////////////////////////////////////////////////////////////////////////////
110// Caching
111///////////////////////////////////////////////////////////////////////////////
112
Romain Guy42e1e0d2012-07-30 14:47:51 -0700113Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
Romain Guyc0ac1932010-07-19 18:43:02 -0700114
Romain Guy42e1e0d2012-07-30 14:47:51 -0700115 GradientCacheEntry gradient(colors, positions, count);
Romain Guy6203f6c2011-08-01 18:56:21 -0700116 Texture* texture = mCache.get(gradient);
Romain Guyc0ac1932010-07-19 18:43:02 -0700117
Romain Guy6203f6c2011-08-01 18:56:21 -0700118 if (!texture) {
Romain Guy42e1e0d2012-07-30 14:47:51 -0700119 texture = addLinearGradient(gradient, colors, positions, count);
Romain Guyfe48f652010-11-11 15:36:56 -0800120 }
Romain Guy6203f6c2011-08-01 18:56:21 -0700121
122 return texture;
Romain Guyfe48f652010-11-11 15:36:56 -0800123}
124
125void GradientCache::clear() {
Romain Guyc0ac1932010-07-19 18:43:02 -0700126 mCache.clear();
127}
128
Romain Guy42e1e0d2012-07-30 14:47:51 -0700129void 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
Romain Guy6203f6c2011-08-01 18:56:21 -0700145Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
Romain Guy42e1e0d2012-07-30 14:47:51 -0700146 uint32_t* colors, float* positions, int count) {
Romain Guy8dcfd5e2012-07-20 11:36:03 -0700147
Romain Guy42e1e0d2012-07-30 14:47:51 -0700148 GradientInfo info;
149 getGradientInfo(colors, count, info);
Romain Guyc0ac1932010-07-19 18:43:02 -0700150
Romain Guy42e1e0d2012-07-30 14:47:51 -0700151 Texture* texture = new Texture;
152 texture->width = info.width;
153 texture->height = GRADIENT_TEXTURE_HEIGHT;
154 texture->blend = info.hasAlpha;
155 texture->generation = 1;
Romain Guyc0ac1932010-07-19 18:43:02 -0700156
157 // Asume the cache is always big enough
Romain Guy42e1e0d2012-07-30 14:47:51 -0700158 const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
Romain Guyc0ac1932010-07-19 18:43:02 -0700159 while (mSize + size > mMaxSize) {
160 mCache.removeOldest();
161 }
162
Romain Guy42e1e0d2012-07-30 14:47:51 -0700163 generateTexture(colors, positions, count, texture);
Romain Guyc0ac1932010-07-19 18:43:02 -0700164
165 mSize += size;
Romain Guy6203f6c2011-08-01 18:56:21 -0700166 mCache.put(gradient, texture);
Romain Guyc0ac1932010-07-19 18:43:02 -0700167
168 return texture;
169}
170
Romain Guy42e1e0d2012-07-30 14:47:51 -0700171void 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);
Romain Guyc0ac1932010-07-19 18:43:02 -0700219 }
220
Romain Guy42e1e0d2012-07-30 14:47:51 -0700221 for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
222 memcpy(pixels + width * i, pixels, rowBytes);
223 }
Romain Guyc0ac1932010-07-19 18:43:02 -0700224
225 glGenTextures(1, &texture->id);
226
227 glBindTexture(GL_TEXTURE_2D, texture->id);
Romain Guy42e1e0d2012-07-30 14:47:51 -0700228 glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
Romain Guyc0ac1932010-07-19 18:43:02 -0700229
Romain Guy42e1e0d2012-07-30 14:47:51 -0700230 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
231 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
Romain Guyc0ac1932010-07-19 18:43:02 -0700232
Romain Guy39d252a2011-12-12 18:14:06 -0800233 texture->setFilter(GL_LINEAR);
234 texture->setWrap(GL_CLAMP_TO_EDGE);
Romain Guyc0ac1932010-07-19 18:43:02 -0700235}
236
237}; // namespace uirenderer
238}; // namespace android