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