1/*
2 ** Copyright 2006, 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#include <stdio.h>
18#include <stdlib.h>
19#include "context.h"
20#include "TextureObjectManager.h"
21
22namespace android {
23// ----------------------------------------------------------------------------
24
25EGLTextureObject::EGLTextureObject()
26    : mSize(0)
27{
28    init();
29}
30
31EGLTextureObject::~EGLTextureObject()
32{
33    if (!direct) {
34        if (mSize && surface.data)
35            free(surface.data);
36        if (mMipmaps)
37            freeMipmaps();
38    }
39}
40
41void EGLTextureObject::init()
42{
43    memset(&surface, 0, sizeof(surface));
44    surface.version = sizeof(surface);
45    mMipmaps = 0;
46    mNumExtraLod = 0;
47    mIsComplete = false;
48    wraps = GL_REPEAT;
49    wrapt = GL_REPEAT;
50    min_filter = GL_LINEAR;
51    mag_filter = GL_LINEAR;
52    internalformat = 0;
53    memset(crop_rect, 0, sizeof(crop_rect));
54    generate_mipmap = GL_FALSE;
55    direct = GL_FALSE;
56    buffer = 0;
57}
58
59void EGLTextureObject::copyParameters(const sp<EGLTextureObject>& old)
60{
61    wraps = old->wraps;
62    wrapt = old->wrapt;
63    min_filter = old->min_filter;
64    mag_filter = old->mag_filter;
65    memcpy(crop_rect, old->crop_rect, sizeof(crop_rect));
66    generate_mipmap = old->generate_mipmap;
67    direct = old->direct;
68}
69
70status_t EGLTextureObject::allocateMipmaps()
71{
72    // here, by construction, mMipmaps=0 && mNumExtraLod=0
73
74    if (!surface.data)
75        return NO_INIT;
76
77    int w = surface.width;
78    int h = surface.height;
79    const int numLods = 31 - gglClz(max(w,h));
80    if (numLods <= 0)
81        return NO_ERROR;
82
83    mMipmaps = (GGLSurface*)malloc(numLods * sizeof(GGLSurface));
84    if (!mMipmaps)
85        return NO_MEMORY;
86
87    memset(mMipmaps, 0, numLods * sizeof(GGLSurface));
88    mNumExtraLod = numLods;
89    return NO_ERROR;
90}
91
92void EGLTextureObject::freeMipmaps()
93{
94    if (mMipmaps) {
95        for (int i=0 ; i<mNumExtraLod ; i++) {
96            if (mMipmaps[i].data) {
97                free(mMipmaps[i].data);
98            }
99        }
100        free(mMipmaps);
101        mMipmaps = 0;
102        mNumExtraLod = 0;
103    }
104}
105
106const GGLSurface& EGLTextureObject::mip(int lod) const
107{
108    if (lod<=0 || !mMipmaps)
109        return surface;
110    lod = min(lod-1, mNumExtraLod-1);
111    return mMipmaps[lod];
112}
113
114GGLSurface& EGLTextureObject::editMip(int lod)
115{
116    return const_cast<GGLSurface&>(mip(lod));
117}
118
119status_t EGLTextureObject::setSurface(GGLSurface const* s)
120{
121    // XXX: glFlush() on 's'
122    if (mSize && surface.data) {
123        free(surface.data);
124    }
125    surface = *s;
126    internalformat = 0;
127    buffer = 0;
128
129    // we should keep the crop_rect, but it's delicate because
130    // the new size of the surface could make it invalid.
131    // so for now, we just loose it.
132    memset(crop_rect, 0, sizeof(crop_rect));
133
134    // it would be nice if we could keep the generate_mipmap flag,
135    // we would have to generate them right now though.
136    generate_mipmap = GL_FALSE;
137
138    direct = GL_TRUE;
139    mSize = 0;  // we don't own this surface
140    if (mMipmaps)
141        freeMipmaps();
142    mIsComplete = true;
143    return NO_ERROR;
144}
145
146status_t EGLTextureObject::setImage(ANativeWindowBuffer* native_buffer)
147{
148    GGLSurface sur;
149    sur.version = sizeof(GGLSurface);
150    sur.width = native_buffer->width;
151    sur.height= native_buffer->height;
152    sur.stride= native_buffer->stride;
153    sur.format= native_buffer->format;
154    sur.data  = 0;
155    setSurface(&sur);
156    buffer = native_buffer;
157    return NO_ERROR;
158}
159
160status_t EGLTextureObject::reallocate(
161        GLint level, int w, int h, int s,
162        int format, int compressedFormat, int bpr)
163{
164    const size_t size = h * bpr;
165    if (level == 0)
166    {
167        if (size!=mSize || !surface.data) {
168            if (mSize && surface.data) {
169                free(surface.data);
170            }
171            surface.data = (GGLubyte*)malloc(size);
172            if (!surface.data) {
173                mSize = 0;
174                mIsComplete = false;
175                return NO_MEMORY;
176            }
177            mSize = size;
178        }
179        surface.version = sizeof(GGLSurface);
180        surface.width  = w;
181        surface.height = h;
182        surface.stride = s;
183        surface.format = format;
184        surface.compressedFormat = compressedFormat;
185        if (mMipmaps)
186            freeMipmaps();
187        mIsComplete = true;
188    }
189    else
190    {
191        if (!mMipmaps) {
192            if (allocateMipmaps() != NO_ERROR)
193                return NO_MEMORY;
194        }
195
196        ALOGW_IF(level-1 >= mNumExtraLod,
197                "specifying mipmap level %d, but # of level is %d",
198                level, mNumExtraLod+1);
199
200        GGLSurface& mipmap = editMip(level);
201        if (mipmap.data)
202            free(mipmap.data);
203
204        mipmap.data = (GGLubyte*)malloc(size);
205        if (!mipmap.data) {
206            memset(&mipmap, 0, sizeof(GGLSurface));
207            mIsComplete = false;
208            return NO_MEMORY;
209        }
210
211        mipmap.version = sizeof(GGLSurface);
212        mipmap.width  = w;
213        mipmap.height = h;
214        mipmap.stride = s;
215        mipmap.format = format;
216        mipmap.compressedFormat = compressedFormat;
217
218        // check if the texture is complete
219        mIsComplete = true;
220        const GGLSurface* prev = &surface;
221        for (int i=0 ; i<mNumExtraLod ; i++) {
222            const GGLSurface* curr = mMipmaps + i;
223            if (curr->format != surface.format) {
224                mIsComplete = false;
225                break;
226            }
227
228            uint32_t w = (prev->width  >> 1) ? : 1;
229            uint32_t h = (prev->height >> 1) ? : 1;
230            if (w != curr->width || h != curr->height) {
231                mIsComplete = false;
232                break;
233            }
234            prev = curr;
235        }
236    }
237    return NO_ERROR;
238}
239
240// ----------------------------------------------------------------------------
241
242EGLSurfaceManager::EGLSurfaceManager()
243    : TokenManager()
244{
245}
246
247EGLSurfaceManager::~EGLSurfaceManager()
248{
249    // everything gets freed automatically here...
250}
251
252sp<EGLTextureObject> EGLSurfaceManager::createTexture(GLuint name)
253{
254    sp<EGLTextureObject> result;
255
256    Mutex::Autolock _l(mLock);
257    if (mTextures.indexOfKey(name) >= 0)
258        return result; // already exists!
259
260    result = new EGLTextureObject();
261
262    status_t err = mTextures.add(name, result);
263    if (err < 0)
264        result.clear();
265
266    return result;
267}
268
269sp<EGLTextureObject> EGLSurfaceManager::removeTexture(GLuint name)
270{
271    Mutex::Autolock _l(mLock);
272    const ssize_t index = mTextures.indexOfKey(name);
273    if (index >= 0) {
274        sp<EGLTextureObject> result(mTextures.valueAt(index));
275        mTextures.removeItemsAt(index);
276        return result;
277    }
278    return 0;
279}
280
281sp<EGLTextureObject> EGLSurfaceManager::replaceTexture(GLuint name)
282{
283    sp<EGLTextureObject> tex;
284    Mutex::Autolock _l(mLock);
285    const ssize_t index = mTextures.indexOfKey(name);
286    if (index >= 0) {
287        const sp<EGLTextureObject>& old = mTextures.valueAt(index);
288        const uint32_t refs = old->getStrongCount();
289        if (ggl_likely(refs == 1)) {
290            // we're the only owner
291            tex = old;
292        } else {
293            // keep the texture's parameters
294            tex = new EGLTextureObject();
295            tex->copyParameters(old);
296            mTextures.removeItemsAt(index);
297            mTextures.add(name, tex);
298        }
299    }
300    return tex;
301}
302
303void EGLSurfaceManager::deleteTextures(GLsizei n, const GLuint *tokens)
304{
305    // free all textures
306    Mutex::Autolock _l(mLock);
307    for (GLsizei i=0 ; i<n ; i++) {
308        const GLuint t(*tokens++);
309        if (t) {
310            mTextures.removeItem(t);
311        }
312    }
313}
314
315sp<EGLTextureObject> EGLSurfaceManager::texture(GLuint name)
316{
317    Mutex::Autolock _l(mLock);
318    const ssize_t index = mTextures.indexOfKey(name);
319    if (index >= 0)
320        return mTextures.valueAt(index);
321    return 0;
322}
323
324// ----------------------------------------------------------------------------
325}; // namespace android
326