scummvm/graphics/tinygl/texture.cpp
2014-08-13 18:52:52 +02:00

292 lines
7.1 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-1998 Fabrice Bellard,
* which is licensed under the zlib-license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
// Texture Manager
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
static GLTexture *find_texture(GLContext *c, int h) {
GLTexture *t;
t = c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
while (t) {
if (t->handle == h)
return t;
t = t->next;
}
return NULL;
}
void free_texture(GLContext *c, int h) {
GLTexture *t, **ht;
GLImage *im;
t = find_texture(c, h);
if (!t->prev) {
ht = &c->shared_state.texture_hash_table[t->handle % TEXTURE_HASH_TABLE_SIZE];
*ht = t->next;
} else {
t->prev->next = t->next;
}
if (t->next)
t->next->prev = t->prev;
for (int i = 0; i < MAX_TEXTURE_LEVELS; i++) {
im = &t->images[i];
if (im->pixmap)
im->pixmap.free();
}
gl_free(t);
}
GLTexture *alloc_texture(GLContext *c, int h) {
GLTexture *t, **ht;
t = (GLTexture *)gl_zalloc(sizeof(GLTexture));
ht = &c->shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
t->next = *ht;
t->prev = NULL;
if (t->next)
t->next->prev = t;
*ht = t;
t->handle = h;
return t;
}
void glInitTextures(GLContext *c) {
// textures
c->texture_2d_enabled = 0;
c->current_texture = find_texture(c, 0);
}
void glopBindTexture(GLContext *c, GLParam *p) {
int target = p[1].i;
int texture = p[2].i;
GLTexture *t;
assert(target == TGL_TEXTURE_2D && texture >= 0);
t = find_texture(c, texture);
if (!t) {
t = alloc_texture(c, texture);
}
c->current_texture = t;
}
void glopTexImage2D(GLContext *c, GLParam *p) {
int target = p[1].i;
int level = p[2].i;
int components = p[3].i;
int width = p[4].i;
int height = p[5].i;
int border = p[6].i;
int format = p[7].i;
int type = p[8].i;
byte *pixels = (byte *)p[9].p;
GLImage *im;
byte *pixels1;
bool do_free_after_rgb2rgba = false;
Graphics::PixelFormat sourceFormat;
switch (format) {
case TGL_RGBA:
sourceFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
break;
case TGL_RGB:
sourceFormat = Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0);
break;
case TGL_BGRA:
sourceFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
break;
case TGL_BGR:
sourceFormat = Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
break;
default:
error("tglTexImage2D: Pixel format not handled.");
}
Graphics::PixelFormat pf;
switch (format) {
case TGL_RGBA:
case TGL_RGB:
#if defined(SCUMM_BIG_ENDIAN)
pf = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
#elif defined(SCUMM_LITTLE_ENDIAN)
pf = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
#endif
break;
case TGL_BGRA:
case TGL_BGR:
#if defined(SCUMM_BIG_ENDIAN)
pf = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 0, 8, 16);
#elif defined(SCUMM_LITTLE_ENDIAN)
pf = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
#endif
break;
default:
break;
}
int bytes = pf.bytesPerPixel;
// Simply unpack RGB into RGBA with 255 for Alpha.
// FIXME: This will need additional checks when we get around to adding 24/32-bit backend.
if (target == TGL_TEXTURE_2D && level == 0 && components == 3 && border == 0 && pixels != NULL) {
if (format == TGL_RGB || format == TGL_BGR) {
Graphics::PixelBuffer temp(pf, width * height, DisposeAfterUse::NO);
Graphics::PixelBuffer pixPtr(sourceFormat, pixels);
for (int i = 0; i < width * height; ++i) {
uint8 r, g, b;
pixPtr.getRGBAt(i, r, g, b);
temp.setPixelAt(i, 255, r, g, b);
}
pixels = temp.getRawBuffer();
do_free_after_rgb2rgba = true;
}
} else if (format != TGL_RGBA || type != TGL_UNSIGNED_BYTE) {
error("tglTexImage2D: combination of parameters not handled");
}
pixels1 = new byte[c->_textureSize * c->_textureSize * bytes];
if (pixels != NULL) {
if (width != c->_textureSize || height != c->_textureSize) {
// we use interpolation for better looking result
gl_resizeImage(pixels1, c->_textureSize, c->_textureSize, pixels, width, height);
width = c->_textureSize;
height = c->_textureSize;
} else {
memcpy(pixels1, pixels, c->_textureSize * c->_textureSize * bytes);
}
}
im = &c->current_texture->images[level];
im->xsize = width;
im->ysize = height;
if (im->pixmap)
im->pixmap.free();
im->pixmap = Graphics::PixelBuffer(pf, pixels1);
if (do_free_after_rgb2rgba) {
// pixels as been assigned to tmp.getRawBuffer() which was created with
// DisposeAfterUse::NO, therefore delete[] it
delete[] pixels;
}
}
// TODO: not all tests are done
void glopTexEnv(GLContext *, GLParam *p) {
int target = p[1].i;
int pname = p[2].i;
int param = p[3].i;
if (target != TGL_TEXTURE_ENV) {
error:
error("glTexParameter: unsupported option");
}
if (pname != TGL_TEXTURE_ENV_MODE)
goto error;
if (param != TGL_DECAL)
goto error;
}
// TODO: not all tests are done
void glopTexParameter(GLContext *, GLParam *p) {
int target = p[1].i;
int pname = p[2].i;
int param = p[3].i;
if (target != TGL_TEXTURE_2D) {
error:
error("glTexParameter: unsupported option");
}
switch (pname) {
case TGL_TEXTURE_WRAP_S:
case TGL_TEXTURE_WRAP_T:
if (param != TGL_REPEAT)
goto error;
break;
default:
;
}
}
void glopPixelStore(GLContext *, GLParam *p) {
int pname = p[1].i;
int param = p[2].i;
if (pname != TGL_UNPACK_ALIGNMENT || param != 1) {
error("glPixelStore: unsupported option");
}
}
} // end of namespace TinyGL
void tglGenTextures(int n, unsigned int *textures) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
int max;
TinyGL::GLTexture *t;
max = 0;
for (int i = 0; i < TEXTURE_HASH_TABLE_SIZE; i++) {
t = c->shared_state.texture_hash_table[i];
while (t) {
if (t->handle > max)
max = t->handle;
t = t->next;
}
}
for (int i = 0; i < n; i++) {
textures[i] = max + i + 1;
}
}
void tglDeleteTextures(int n, const unsigned int *textures) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
TinyGL::GLTexture *t;
for (int i = 0; i < n; i++) {
t = TinyGL::find_texture(c, textures[i]);
if (t) {
if (t == c->current_texture) {
tglBindTexture(TGL_TEXTURE_2D, 0);
}
TinyGL::free_texture(c, textures[i]);
}
}
}