Home | History | Annotate | Download | only in hwui
      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 
     26 namespace android {
     27 namespace uirenderer {
     28 
     29 ///////////////////////////////////////////////////////////////////////////////
     30 // Functions
     31 ///////////////////////////////////////////////////////////////////////////////
     32 
     33 template<typename T>
     34 static inline T min(T a, T b) {
     35     return a < b ? a : b;
     36 }
     37 
     38 ///////////////////////////////////////////////////////////////////////////////
     39 // Cache entry
     40 ///////////////////////////////////////////////////////////////////////////////
     41 
     42 hash_t GradientCacheEntry::hash() const {
     43     uint32_t hash = JenkinsHashMix(0, count);
     44     for (uint32_t i = 0; i < count; i++) {
     45         hash = JenkinsHashMix(hash, android::hash_type(colors[i]));
     46         hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
     47     }
     48     return JenkinsHashWhiten(hash);
     49 }
     50 
     51 int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
     52     int deltaInt = int(lhs.count) - int(rhs.count);
     53     if (deltaInt != 0) return deltaInt;
     54 
     55     deltaInt = memcmp(lhs.colors, rhs.colors, lhs.count * sizeof(uint32_t));
     56     if (deltaInt != 0) return deltaInt;
     57 
     58     return memcmp(lhs.positions, rhs.positions, lhs.count * sizeof(float));
     59 }
     60 
     61 ///////////////////////////////////////////////////////////////////////////////
     62 // Constructors/destructor
     63 ///////////////////////////////////////////////////////////////////////////////
     64 
     65 GradientCache::GradientCache():
     66         mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
     67         mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
     68     char property[PROPERTY_VALUE_MAX];
     69     if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
     70         INIT_LOGD("  Setting gradient cache size to %sMB", property);
     71         setMaxSize(MB(atof(property)));
     72     } else {
     73         INIT_LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
     74     }
     75 
     76     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     77 
     78     mCache.setOnEntryRemovedListener(this);
     79 
     80     const Extensions& extensions = Extensions::getInstance();
     81     mUseFloatTexture = extensions.hasFloatTextures();
     82     mHasNpot = extensions.hasNPot();
     83 }
     84 
     85 GradientCache::GradientCache(uint32_t maxByteSize):
     86         mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
     87         mSize(0), mMaxSize(maxByteSize) {
     88     mCache.setOnEntryRemovedListener(this);
     89 }
     90 
     91 GradientCache::~GradientCache() {
     92     mCache.clear();
     93 }
     94 
     95 ///////////////////////////////////////////////////////////////////////////////
     96 // Size management
     97 ///////////////////////////////////////////////////////////////////////////////
     98 
     99 uint32_t GradientCache::getSize() {
    100     return mSize;
    101 }
    102 
    103 uint32_t GradientCache::getMaxSize() {
    104     return mMaxSize;
    105 }
    106 
    107 void GradientCache::setMaxSize(uint32_t maxSize) {
    108     mMaxSize = maxSize;
    109     while (mSize > mMaxSize) {
    110         mCache.removeOldest();
    111     }
    112 }
    113 
    114 ///////////////////////////////////////////////////////////////////////////////
    115 // Callbacks
    116 ///////////////////////////////////////////////////////////////////////////////
    117 
    118 void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) {
    119     if (texture) {
    120         const uint32_t size = texture->width * texture->height * bytesPerPixel();
    121         mSize -= size;
    122 
    123         texture->deleteTexture();
    124         delete texture;
    125     }
    126 }
    127 
    128 ///////////////////////////////////////////////////////////////////////////////
    129 // Caching
    130 ///////////////////////////////////////////////////////////////////////////////
    131 
    132 Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
    133     GradientCacheEntry gradient(colors, positions, count);
    134     Texture* texture = mCache.get(gradient);
    135 
    136     if (!texture) {
    137         texture = addLinearGradient(gradient, colors, positions, count);
    138     }
    139 
    140     return texture;
    141 }
    142 
    143 void GradientCache::clear() {
    144     mCache.clear();
    145 }
    146 
    147 void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
    148         GradientInfo& info) {
    149     uint32_t width = 256 * (count - 1);
    150 
    151     // If the npot extension is not supported we cannot use non-clamp
    152     // wrap modes. We therefore find the nearest largest power of 2
    153     // unless width is already a power of 2
    154     if (!mHasNpot && (width & (width - 1)) != 0) {
    155         width = 1 << (32 - __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 
    170 Texture* 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 = 2;
    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 * bytesPerPixel();
    184     while (getSize() + size > mMaxSize) {
    185         mCache.removeOldest();
    186     }
    187 
    188     generateTexture(colors, positions, texture);
    189 
    190     mSize += size;
    191     mCache.put(gradient, texture);
    192 
    193     return texture;
    194 }
    195 
    196 size_t GradientCache::bytesPerPixel() const {
    197     // We use 4 channels (RGBA)
    198     return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
    199 }
    200 
    201 void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
    202     outColor.r = (inColor >> 16) & 0xff;
    203     outColor.g = (inColor >>  8) & 0xff;
    204     outColor.b = (inColor >>  0) & 0xff;
    205     outColor.a = (inColor >> 24) & 0xff;
    206 }
    207 
    208 void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
    209     outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
    210     outColor.g = ((inColor >>  8) & 0xff) / 255.0f;
    211     outColor.b = ((inColor >>  0) & 0xff) / 255.0f;
    212     outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
    213 }
    214 
    215 void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
    216         uint8_t*& dst) const {
    217     float oppAmount = 1.0f - amount;
    218     const float alpha = start.a * oppAmount + end.a * amount;
    219     const float a = alpha / 255.0f;
    220 
    221     *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
    222     *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
    223     *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
    224     *dst++ = uint8_t(alpha);
    225 }
    226 
    227 void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
    228         uint8_t*& dst) const {
    229     float oppAmount = 1.0f - amount;
    230     const float a = start.a * oppAmount + end.a * amount;
    231 
    232     float* d = (float*) dst;
    233     *d++ = a * (start.r * oppAmount + end.r * amount);
    234     *d++ = a * (start.g * oppAmount + end.g * amount);
    235     *d++ = a * (start.b * oppAmount + end.b * amount);
    236     *d++ = a;
    237 
    238     dst += 4 * sizeof(float);
    239 }
    240 
    241 void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) {
    242     const uint32_t width = texture->width;
    243     const GLsizei rowBytes = width * bytesPerPixel();
    244     uint8_t pixels[rowBytes * texture->height];
    245 
    246     static ChannelSplitter gSplitters[] = {
    247             &android::uirenderer::GradientCache::splitToBytes,
    248             &android::uirenderer::GradientCache::splitToFloats,
    249     };
    250     ChannelSplitter split = gSplitters[mUseFloatTexture];
    251 
    252     static ChannelMixer gMixers[] = {
    253             &android::uirenderer::GradientCache::mixBytes,
    254             &android::uirenderer::GradientCache::mixFloats,
    255     };
    256     ChannelMixer mix = gMixers[mUseFloatTexture];
    257 
    258     GradientColor start;
    259     (this->*split)(colors[0], start);
    260 
    261     GradientColor end;
    262     (this->*split)(colors[1], end);
    263 
    264     int currentPos = 1;
    265     float startPos = positions[0];
    266     float distance = positions[1] - startPos;
    267 
    268     uint8_t* dst = pixels;
    269     for (uint32_t x = 0; x < width; x++) {
    270         float pos = x / float(width - 1);
    271         if (pos > positions[currentPos]) {
    272             start = end;
    273             startPos = positions[currentPos];
    274 
    275             currentPos++;
    276 
    277             (this->*split)(colors[currentPos], end);
    278             distance = positions[currentPos] - startPos;
    279         }
    280 
    281         float amount = (pos - startPos) / distance;
    282         (this->*mix)(start, end, amount, dst);
    283     }
    284 
    285     memcpy(pixels + rowBytes, pixels, rowBytes);
    286 
    287     glGenTextures(1, &texture->id);
    288     Caches::getInstance().bindTexture(texture->id);
    289     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    290 
    291     if (mUseFloatTexture) {
    292         // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
    293         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
    294                 GL_RGBA, GL_FLOAT, pixels);
    295     } else {
    296         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
    297                 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    298     }
    299 
    300     texture->setFilter(GL_LINEAR);
    301     texture->setWrap(GL_CLAMP_TO_EDGE);
    302 }
    303 
    304 }; // namespace uirenderer
    305 }; // namespace android
    306