TextureCache.cpp revision 163935113919a184122b8b3bd672ef08c8df65dc
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 <GLES2/gl2.h>
20
21#include "TextureCache.h"
22
23namespace android {
24namespace uirenderer {
25
26///////////////////////////////////////////////////////////////////////////////
27// Constructors/destructor
28///////////////////////////////////////////////////////////////////////////////
29
30TextureCache::TextureCache(uint32_t maxByteSize):
31        mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
32        mSize(0), mMaxSize(maxByteSize) {
33    mCache.setOnEntryRemovedListener(this);
34
35    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
36    LOGD("Maximum texture dimension is %d pixels", mMaxTextureSize);
37}
38
39TextureCache::~TextureCache() {
40    mCache.clear();
41}
42
43///////////////////////////////////////////////////////////////////////////////
44// Size management
45///////////////////////////////////////////////////////////////////////////////
46
47uint32_t TextureCache::getSize() {
48    return mSize;
49}
50
51uint32_t TextureCache::getMaxSize() {
52    return mMaxSize;
53}
54
55void TextureCache::setMaxSize(uint32_t maxSize) {
56    mMaxSize = maxSize;
57    while (mSize > mMaxSize) {
58        mCache.removeOldest();
59    }
60}
61
62///////////////////////////////////////////////////////////////////////////////
63// Callbacks
64///////////////////////////////////////////////////////////////////////////////
65
66void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
67    if (bitmap) {
68        const uint32_t size = bitmap->rowBytes() * bitmap->height();
69        mSize -= size;
70    }
71
72    if (texture) {
73        glDeleteTextures(1, &texture->id);
74        delete texture;
75    }
76}
77
78///////////////////////////////////////////////////////////////////////////////
79// Caching
80///////////////////////////////////////////////////////////////////////////////
81
82Texture* TextureCache::get(SkBitmap* bitmap) {
83    Texture* texture = mCache.get(bitmap);
84    if (!texture) {
85        if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
86            LOGW("Bitmap too large to be uploaded into a texture");
87            return NULL;
88        }
89
90        const uint32_t size = bitmap->rowBytes() * bitmap->height();
91        // Don't even try to cache a bitmap that's bigger than the cache
92        if (size < mMaxSize) {
93            while (mSize + size > mMaxSize) {
94                mCache.removeOldest();
95            }
96        }
97
98        texture = new Texture;
99        generateTexture(bitmap, texture, false);
100
101        if (size < mMaxSize) {
102            mSize += size;
103            mCache.put(bitmap, texture);
104        } else {
105            texture->cleanup = true;
106        }
107    } else if (bitmap->getGenerationID() != texture->generation) {
108        generateTexture(bitmap, texture, true);
109    }
110
111    return texture;
112}
113
114void TextureCache::remove(SkBitmap* bitmap) {
115    mCache.remove(bitmap);
116}
117
118void TextureCache::clear() {
119    mCache.clear();
120}
121
122void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
123    SkAutoLockPixels alp(*bitmap);
124    if (!bitmap->readyToDraw()) {
125        LOGE("Cannot generate texture from bitmap");
126        return;
127    }
128
129    if (!regenerate) {
130        texture->generation = bitmap->getGenerationID();
131        texture->width = bitmap->width();
132        texture->height = bitmap->height();
133
134        glGenTextures(1, &texture->id);
135    }
136
137    glBindTexture(GL_TEXTURE_2D, texture->id);
138    glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
139
140    switch (bitmap->getConfig()) {
141    case SkBitmap::kA8_Config:
142        texture->blend = true;
143        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, 0,
144                GL_ALPHA, GL_UNSIGNED_BYTE, bitmap->getPixels());
145        break;
146    case SkBitmap::kRGB_565_Config:
147        texture->blend = false;
148        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmap->rowBytesAsPixels(), texture->height, 0,
149                GL_RGB, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
150        break;
151    case SkBitmap::kARGB_8888_Config:
152        texture->blend = !bitmap->isOpaque();
153        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
154                GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
155        break;
156    default:
157        break;
158    }
159
160    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
161    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
162
163    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
164    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
165}
166
167}; // namespace uirenderer
168}; // namespace android
169