/*
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include "context.h"
#include "TextureObjectManager.h"
#include <private/ui/android_natives_priv.h>
namespace android {
// ----------------------------------------------------------------------------
EGLTextureObject::EGLTextureObject()
: mSize(0)
{
init();
}
EGLTextureObject::~EGLTextureObject()
{
if (!direct) {
if (mSize && surface.data)
free(surface.data);
if (mMipmaps)
freeMipmaps();
}
}
void EGLTextureObject::init()
{
memset(&surface, 0, sizeof(surface));
surface.version = sizeof(surface);
mMipmaps = 0;
mNumExtraLod = 0;
mIsComplete = false;
wraps = GL_REPEAT;
wrapt = GL_REPEAT;
min_filter = GL_LINEAR;
mag_filter = GL_LINEAR;
internalformat = 0;
memset(crop_rect, 0, sizeof(crop_rect));
generate_mipmap = GL_FALSE;
direct = GL_FALSE;
buffer = 0;
}
void EGLTextureObject::copyParameters(const sp<EGLTextureObject>& old)
{
wraps = old->wraps;
wrapt = old->wrapt;
min_filter = old->min_filter;
mag_filter = old->mag_filter;
memcpy(crop_rect, old->crop_rect, sizeof(crop_rect));
generate_mipmap = old->generate_mipmap;
direct = old->direct;
}
status_t EGLTextureObject::allocateMipmaps()
{
// here, by construction, mMipmaps=0 && mNumExtraLod=0
if (!surface.data)
return NO_INIT;
int w = surface.width;
int h = surface.height;
const int numLods = 31 - gglClz(max(w,h));
if (numLods <= 0)
return NO_ERROR;
mMipmaps = (GGLSurface*)malloc(numLods * sizeof(GGLSurface));
if (!mMipmaps)
return NO_MEMORY;
memset(mMipmaps, 0, numLods * sizeof(GGLSurface));
mNumExtraLod = numLods;
return NO_ERROR;
}
void EGLTextureObject::freeMipmaps()
{
if (mMipmaps) {
for (int i=0 ; i<mNumExtraLod ; i++) {
if (mMipmaps[i].data) {
free(mMipmaps[i].data);
}
}
free(mMipmaps);
mMipmaps = 0;
mNumExtraLod = 0;
}
}
const GGLSurface& EGLTextureObject::mip(int lod) const
{
if (lod<=0 || !mMipmaps)
return surface;
lod = min(lod-1, mNumExtraLod-1);
return mMipmaps[lod];
}
GGLSurface& EGLTextureObject::editMip(int lod)
{
return const_cast<GGLSurface&>(mip(lod));
}
status_t EGLTextureObject::setSurface(GGLSurface const* s)
{
// XXX: glFlush() on 's'
if (mSize && surface.data) {
free(surface.data);
}
surface = *s;
internalformat = 0;
buffer = 0;
// we should keep the crop_rect, but it's delicate because
// the new size of the surface could make it invalid.
// so for now, we just loose it.
memset(crop_rect, 0, sizeof(crop_rect));
// it would be nice if we could keep the generate_mipmap flag,
// we would have to generate them right now though.
generate_mipmap = GL_FALSE;
direct = GL_TRUE;
mSize = 0; // we don't own this surface
if (mMipmaps)
freeMipmaps();
mIsComplete = true;
return NO_ERROR;
}
status_t EGLTextureObject::setImage(android_native_buffer_t* native_buffer)
{
GGLSurface sur;
sur.version = sizeof(GGLSurface);
sur.width = native_buffer->width;
sur.height= native_buffer->height;
sur.stride= native_buffer->stride;
sur.format= native_buffer->format;
sur.data = 0;
setSurface(&sur);
buffer = native_buffer;
return NO_ERROR;
}
status_t EGLTextureObject::reallocate(
GLint level, int w, int h, int s,
int format, int compressedFormat, int bpr)
{
const size_t size = h * bpr;
if (level == 0)
{
if (size!=mSize || !surface.data) {
if (mSize && surface.data) {
free(surface.data);
}
surface.data = (GGLubyte*)malloc(size);
if (!surface.data) {
mSize = 0;
mIsComplete = false;
return NO_MEMORY;
}
mSize = size;
}
surface.version = sizeof(GGLSurface);
surface.width = w;
surface.height = h;
surface.stride = s;
surface.format = format;
surface.compressedFormat = compressedFormat;
if (mMipmaps)
freeMipmaps();
mIsComplete = true;
}
else
{
if (!mMipmaps) {
if (allocateMipmaps() != NO_ERROR)
return NO_MEMORY;
}
LOGW_IF(level-1 >= mNumExtraLod,
"specifying mipmap level %d, but # of level is %d",
level, mNumExtraLod+1);
GGLSurface& mipmap = editMip(level);
if (mipmap.data)
free(mipmap.data);
mipmap.data = (GGLubyte*)malloc(size);
if (!mipmap.data) {
memset(&mipmap, 0, sizeof(GGLSurface));
mIsComplete = false;
return NO_MEMORY;
}
mipmap.version = sizeof(GGLSurface);
mipmap.width = w;
mipmap.height = h;
mipmap.stride = s;
mipmap.format = format;
mipmap.compressedFormat = compressedFormat;
// check if the texture is complete
mIsComplete = true;
const GGLSurface* prev = &surface;
for (int i=0 ; i<mNumExtraLod ; i++) {
const GGLSurface* curr = mMipmaps + i;
if (curr->format != surface.format) {
mIsComplete = false;
break;
}
uint32_t w = (prev->width >> 1) ? : 1;
uint32_t h = (prev->height >> 1) ? : 1;
if (w != curr->width || h != curr->height) {
mIsComplete = false;
break;
}
prev = curr;
}
}
return NO_ERROR;
}
// ----------------------------------------------------------------------------
EGLSurfaceManager::EGLSurfaceManager()
: TokenManager()
{
}
EGLSurfaceManager::~EGLSurfaceManager()
{
// everything gets freed automatically here...
}
sp<EGLTextureObject> EGLSurfaceManager::createTexture(GLuint name)
{
sp<EGLTextureObject> result;
Mutex::Autolock _l(mLock);
if (mTextures.indexOfKey(name) >= 0)
return result; // already exists!
result = new EGLTextureObject();
status_t err = mTextures.add(name, result);
if (err < 0)
result.clear();
return result;
}
sp<EGLTextureObject> EGLSurfaceManager::removeTexture(GLuint name)
{
Mutex::Autolock _l(mLock);
const ssize_t index = mTextures.indexOfKey(name);
if (index >= 0) {
sp<EGLTextureObject> result(mTextures.valueAt(index));
mTextures.removeItemsAt(index);
return result;
}
return 0;
}
sp<EGLTextureObject> EGLSurfaceManager::replaceTexture(GLuint name)
{
sp<EGLTextureObject> tex;
Mutex::Autolock _l(mLock);
const ssize_t index = mTextures.indexOfKey(name);
if (index >= 0) {
const sp<EGLTextureObject>& old = mTextures.valueAt(index);
const uint32_t refs = old->getStrongCount();
if (ggl_likely(refs == 1)) {
// we're the only owner
tex = old;
} else {
// keep the texture's parameters
tex = new EGLTextureObject();
tex->copyParameters(old);
mTextures.removeItemsAt(index);
mTextures.add(name, tex);
}
}
return tex;
}
void EGLSurfaceManager::deleteTextures(GLsizei n, const GLuint *tokens)
{
// free all textures
Mutex::Autolock _l(mLock);
for (GLsizei i=0 ; i<n ; i++) {
const GLuint t(*tokens++);
if (t) {
mTextures.removeItem(t);
}
}
}
sp<EGLTextureObject> EGLSurfaceManager::texture(GLuint name)
{
Mutex::Autolock _l(mLock);
const ssize_t index = mTextures.indexOfKey(name);
if (index >= 0)
return mTextures.valueAt(index);
return 0;
}
// ----------------------------------------------------------------------------
}; // namespace android