1464 lines
52 KiB
C
Executable File
1464 lines
52 KiB
C
Executable File
#include "texture.h"
|
|
#include "raster.h"
|
|
#include "decompress.h"
|
|
#include <EGL/egl.h>
|
|
#include <EGL/eglext.h>
|
|
#include "../glx/streaming.h"
|
|
|
|
#ifndef GL_TEXTURE_STREAM_IMG
|
|
#define GL_TEXTURE_STREAM_IMG 0x8C0D
|
|
#endif
|
|
|
|
// expand non-power-of-two sizes
|
|
// TODO: what does this do to repeating textures?
|
|
int npot(int n) {
|
|
if (n == 0) return 0;
|
|
|
|
int i = 1;
|
|
while (i < n) i <<= 1;
|
|
return i;
|
|
}
|
|
|
|
// conversions for GL_ARB_texture_rectangle
|
|
void tex_coord_rect_arb(GLfloat *tex, GLsizei len,
|
|
GLsizei width, GLsizei height) {
|
|
if (!tex || !width || !height)
|
|
return;
|
|
|
|
GLfloat iwidth, iheight;
|
|
iwidth = 1.0f/width;
|
|
iheight = 1.0f/height;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
tex[0] *= iwidth;
|
|
tex[1] *= iheight;
|
|
tex += 2;
|
|
}
|
|
}
|
|
|
|
void tex_coord_npot(GLfloat *tex, GLsizei len,
|
|
GLsizei width, GLsizei height,
|
|
GLsizei nwidth, GLsizei nheight) {
|
|
if (!tex || !width || !height)
|
|
return;
|
|
|
|
GLfloat wratio = (width / (GLfloat)nwidth);
|
|
GLfloat hratio = (height / (GLfloat)nheight);
|
|
for (int i = 0; i < len; i++) {
|
|
tex[0] *= wratio;
|
|
tex[1] *= hratio;
|
|
tex += 2;
|
|
}
|
|
}
|
|
|
|
/* Setup the texture coordinates
|
|
*
|
|
* Have to check is ARB_RECTANGLE is used
|
|
* Or some NPOT texture used
|
|
* Or SHRINKED texure used
|
|
*/
|
|
void tex_setup_texcoord(GLuint texunit, GLuint len) {
|
|
LOAD_GLES(glTexCoordPointer);
|
|
GLuint old = state.texture.client;
|
|
|
|
static void * tex[8] = {0,0,0,0,0,0,0,0}; // hugly but convenient...
|
|
|
|
if (tex[texunit]) {
|
|
free(tex[texunit]);
|
|
tex[texunit]=NULL;
|
|
}
|
|
|
|
gltexture_t *bound = state.texture.bound[texunit];
|
|
|
|
// check if some changes are needed
|
|
int changes = 0;
|
|
if ((state.texture.rect_arb[texunit]) ||
|
|
(bound && ((bound->width!=bound->nwidth)||(bound->height!=bound->nheight)||
|
|
(bound->shrink && (state.pointers.tex_coord[texunit].type!=GL_FLOAT) && (state.pointers.tex_coord[texunit].type!=GL_DOUBLE)))))
|
|
changes = 1;
|
|
if (old!=texunit) glClientActiveTexture(texunit+GL_TEXTURE0);
|
|
if (changes) {
|
|
// first convert to GLfloat, without normalization
|
|
tex[texunit] = copy_gl_pointer_raw(&state.pointers.tex_coord[texunit], 2, 0, len, state.pointers.tex_coord[texunit].buffer);
|
|
if (!tex[texunit]) {
|
|
printf("LibGL: Error with Texture tranform\n");
|
|
gles_glTexCoordPointer(len, state.pointers.tex_coord[texunit].type, state.pointers.tex_coord[texunit].stride, state.pointers.tex_coord[texunit].pointer);
|
|
if (old!=texunit) glClientActiveTexture(old+GL_TEXTURE0);
|
|
return;
|
|
}
|
|
// Normalize if needed
|
|
if ((state.texture.rect_arb[texunit]) || ((state.pointers.tex_coord[texunit].type!=GL_FLOAT) && (state.pointers.tex_coord[texunit].type!=GL_DOUBLE)))
|
|
tex_coord_rect_arb(tex[texunit], len, bound->width, bound->height);
|
|
if ((bound->width!=bound->nwidth) || (bound->height!=bound->nheight))
|
|
tex_coord_npot(tex[texunit], len, bound->width, bound->height, bound->nwidth, bound->nheight);
|
|
// All done, setup the texcoord array now
|
|
gles_glTexCoordPointer(2, GL_FLOAT, 0, tex[texunit]);
|
|
} else {
|
|
gles_glTexCoordPointer(state.pointers.tex_coord[texunit].size, state.pointers.tex_coord[texunit].type, state.pointers.tex_coord[texunit].stride, state.pointers.tex_coord[texunit].pointer);
|
|
}
|
|
if (old!=texunit) glClientActiveTexture(old+GL_TEXTURE0);
|
|
}
|
|
|
|
static void *swizzle_texture(GLsizei width, GLsizei height,
|
|
GLenum *format, GLenum *type,
|
|
const GLvoid *data) {
|
|
bool convert = false;
|
|
switch (*format) {
|
|
case GL_ALPHA:
|
|
case GL_RGB:
|
|
case GL_RGBA:
|
|
case GL_LUMINANCE:
|
|
case GL_LUMINANCE_ALPHA:
|
|
break;
|
|
default:
|
|
convert = true;
|
|
break;
|
|
}
|
|
switch (*type) {
|
|
//case GL_FLOAT:
|
|
case GL_UNSIGNED_BYTE:
|
|
case GL_UNSIGNED_SHORT_5_6_5:
|
|
case GL_UNSIGNED_SHORT_4_4_4_4:
|
|
case GL_UNSIGNED_SHORT_5_5_5_1:
|
|
break;
|
|
case GL_UNSIGNED_INT_8_8_8_8_REV:
|
|
*type = GL_UNSIGNED_BYTE;
|
|
break;
|
|
default:
|
|
convert = true;
|
|
break;
|
|
}
|
|
if (data) {
|
|
if (convert) {
|
|
GLvoid *pixels = (GLvoid *)data;
|
|
if (! pixel_convert(data, &pixels, width, height,
|
|
*format, *type, GL_RGBA, GL_UNSIGNED_BYTE, 0)) {
|
|
printf("libGL swizzle error: (%#4x, %#4x -> GL_RGBA, UNSIGNED_BYTE)\n",
|
|
*format, *type);
|
|
return NULL;
|
|
}
|
|
*type = GL_UNSIGNED_BYTE;
|
|
*format = GL_RGBA;
|
|
GLvoid *pix2 = pixels;
|
|
if (raster_need_transform())
|
|
if (!pixel_transform(data, &pixels, width, height,
|
|
*format, *type, raster_scale, raster_bias)) {
|
|
printf("libGL swizzle/convert error: (%#4x, %#4x -> GL_RGBA, UNSIGNED_BYTE)\n",
|
|
*format, *type);
|
|
pix2 = pixels;
|
|
}
|
|
if (pix2!=pixels && pixels!=data)
|
|
free(pixels);
|
|
return pix2;
|
|
}
|
|
} else {
|
|
if (convert) {
|
|
*type = GL_UNSIGNED_BYTE;
|
|
*format = GL_RGBA;
|
|
}
|
|
}
|
|
return (void *)data;
|
|
}
|
|
|
|
int automipmap = 0;
|
|
int texcopydata = 0;
|
|
int tested_env = 0;
|
|
int texshrink = 0;
|
|
int texdump = 0;
|
|
int alphahack = 0;
|
|
int texstream = 0;
|
|
|
|
static int proxy_width = 0;
|
|
static int proxy_height = 0;
|
|
|
|
void glTexImage2D(GLenum target, GLint level, GLint internalformat,
|
|
GLsizei width, GLsizei height, GLint border,
|
|
GLenum format, GLenum type, const GLvoid *data) {
|
|
|
|
//printf("glTexImage2D on target=0x%04X with unpack_row_length(%i), size(%i,%i) and skip(%i,%i), format(internal)=%04x(%04x), type=%04x, data=%08x, level=%i (mipmap_need=%i, mipmap_auto=%i) => texture=%u (streamed=%i)\n", target, state.texture.unpack_row_length, width, height, state.texture.unpack_skip_pixels, state.texture.unpack_skip_rows, format, internalformat, type, data, level, (state.texture.bound[state.texture.active])?state.texture.bound[state.texture.active]->mipmap_need:0, (state.texture.bound[state.texture.active])?state.texture.bound[state.texture.active]->mipmap_auto:0, (state.texture.bound[state.texture.active])?state.texture.bound[state.texture.active]->texture:0, (state.texture.bound[state.texture.active])?state.texture.bound[state.texture.active]->streamed:0);
|
|
|
|
// proxy case
|
|
if (target == GL_PROXY_TEXTURE_2D) {
|
|
proxy_width = ((width<<level)>2048)?0:width;
|
|
proxy_height = ((height<<level)>2048)?0:height;
|
|
return;
|
|
}
|
|
//PUSH_IF_COMPILING(glTexImage2D);
|
|
if (state.gl_batch) flush();
|
|
|
|
GLvoid *datab = (GLvoid*)data;
|
|
|
|
if (state.buffers.unpack)
|
|
datab += (uintptr_t)state.buffers.pack->data;
|
|
|
|
GLvoid *pixels = (GLvoid *)datab;
|
|
border = 0; //TODO: something?
|
|
noerrorShim();
|
|
|
|
if (!tested_env) {
|
|
char *env_mipmap = getenv("LIBGL_MIPMAP");
|
|
if (env_mipmap && strcmp(env_mipmap, "1") == 0) {
|
|
automipmap = 1;
|
|
printf("LIBGL: AutoMipMap forced\n");
|
|
}
|
|
if (env_mipmap && strcmp(env_mipmap, "2") == 0) {
|
|
automipmap = 2;
|
|
printf("LIBGL: guess AutoMipMap\n");
|
|
}
|
|
if (env_mipmap && strcmp(env_mipmap, "3") == 0) {
|
|
automipmap = 3;
|
|
printf("LIBGL: ignore MipMap\n");
|
|
}
|
|
char *env_texcopy = getenv("LIBGL_TEXCOPY");
|
|
if (env_texcopy && strcmp(env_texcopy, "1") == 0) {
|
|
texcopydata = 1;
|
|
printf("LIBGL: Texture copy enabled\n");
|
|
}
|
|
char *env_shrink = getenv("LIBGL_SHRINK");
|
|
if (env_shrink && strcmp(env_shrink, "1") == 0) {
|
|
texshrink = 1;
|
|
printf("LIBGL: Texture shink, mode 1 selected (everything / 2)\n");
|
|
}
|
|
if (env_shrink && strcmp(env_shrink, "2") == 0) {
|
|
texshrink = 2;
|
|
printf("LIBGL: Texture shink, mode 2 selected (only > 512 /2 )\n");
|
|
}
|
|
if (env_shrink && strcmp(env_shrink, "3") == 0) {
|
|
texshrink = 3;
|
|
printf("LIBGL: Texture shink, mode 3 selected (only > 256 /2 )\n");
|
|
}
|
|
if (env_shrink && strcmp(env_shrink, "4") == 0) {
|
|
texshrink = 4;
|
|
printf("LIBGL: Texture shink, mode 4 selected (only > 256 /2, >=1024 /4 )\n");
|
|
}
|
|
if (env_shrink && strcmp(env_shrink, "5") == 0) {
|
|
texshrink = 5;
|
|
printf("LIBGL: Texture shink, mode 5 selected (every > 256 is downscaled to 256 )\n");
|
|
}
|
|
if (env_shrink && strcmp(env_shrink, "6") == 0) {
|
|
texshrink = 6;
|
|
printf("LIBGL: Texture shink, mode 6 selected (only > 128 /2, >=512 is downscaled to 256 )\n");
|
|
}
|
|
char *env_dump = getenv("LIBGL_TEXDUMP");
|
|
if (env_dump && strcmp(env_dump, "1") == 0) {
|
|
texdump = 1;
|
|
printf("LIBGL: Texture dump enabled\n");
|
|
}
|
|
char *env_alpha = getenv("LIBGL_ALPHAHACK");
|
|
if (env_alpha && strcmp(env_alpha, "1") == 0) {
|
|
alphahack = 1;
|
|
printf("LIBGL: Alpha Hack enabled\n");
|
|
}
|
|
char *env_stream = getenv("LIBGL_STREAM");
|
|
if (env_stream && strcmp(env_stream, "1") == 0) {
|
|
texstream = InitStreamingCache();
|
|
printf("LIBGL: Streaming texture %s\n",(texstream)?"enabled":"not available");
|
|
//FreeStreamed(AddStreamed(1024, 512, 0));
|
|
}
|
|
if (env_stream && strcmp(env_stream, "2") == 0) {
|
|
texstream = InitStreamingCache()?2:0;
|
|
printf("LIBGL: Streaming texture %s\n",(texstream)?"forced":"not available");
|
|
//FreeStreamed(AddStreamed(1024, 512, 0));
|
|
}
|
|
tested_env = true;
|
|
}
|
|
|
|
gltexture_t *bound = state.texture.bound[state.texture.active];
|
|
if (bound) bound->alpha = pixel_hasalpha(format);
|
|
if (automipmap) {
|
|
if (bound && (level>0))
|
|
if ((automipmap==1) || (automipmap==3) || bound->mipmap_need)
|
|
return; // has been handled by auto_mipmap
|
|
else
|
|
bound->mipmap_need = 1;
|
|
}
|
|
if (datab) {
|
|
|
|
// implements GL_UNPACK_ROW_LENGTH
|
|
if ((state.texture.unpack_row_length && state.texture.unpack_row_length != width) || state.texture.unpack_skip_pixels || state.texture.unpack_skip_rows) {
|
|
int imgWidth, pixelSize;
|
|
pixelSize = pixel_sizeof(format, type);
|
|
imgWidth = ((state.texture.unpack_row_length)? state.texture.unpack_row_length:width) * pixelSize;
|
|
GLubyte *dst = (GLubyte *)malloc(width * height * pixelSize);
|
|
pixels = (GLvoid *)dst;
|
|
const GLubyte *src = (GLubyte *)datab;
|
|
src += state.texture.unpack_skip_pixels * pixelSize + state.texture.unpack_skip_rows * imgWidth;
|
|
for (int y = 0; y < height; y += 1) {
|
|
memcpy(dst, src, width * pixelSize);
|
|
src += imgWidth;
|
|
dst += width;
|
|
}
|
|
}
|
|
|
|
GLvoid *old = pixels;
|
|
pixels = (GLvoid *)swizzle_texture(width, height, &format, &type, old);
|
|
if (old != pixels && old != datab) {
|
|
free(old);
|
|
}
|
|
|
|
if (bound) bound->shrink = 0;
|
|
if (bound && (texshrink==1)) {
|
|
if ((width > 1) && (height > 1)) {
|
|
GLvoid *out = pixels;
|
|
GLfloat ratio = 0.5;
|
|
pixel_scale(pixels, &out, width, height, ratio, format, type);
|
|
if (out != pixels && pixels!=datab)
|
|
free(pixels);
|
|
pixels = out;
|
|
width *= ratio;
|
|
height *= ratio;
|
|
bound->shrink = 1;
|
|
}
|
|
}
|
|
if (bound && (texshrink==2 || texshrink==3)) {
|
|
if (((width%2==0) && (height%2==0)) &&
|
|
((width > ((texshrink==2)?512:256)) && (height > 8)) || ((height > ((texshrink==2)?512:256)) && (width > 8))) {
|
|
GLvoid *out = pixels;
|
|
pixel_halfscale(pixels, &out, width, height, format, type);
|
|
if (out != pixels && pixels!=datab)
|
|
free(pixels);
|
|
pixels = out;
|
|
width /= 2;
|
|
height /= 2;
|
|
bound->shrink = 1;
|
|
}
|
|
}
|
|
if (bound && (texshrink==4)) {
|
|
if (((width%4==0) && (height%4==0)) &&
|
|
((width > 256) && (height > 8)) || ((height > 256) && (width > 8))) {
|
|
if ((width>=1024) || (height>=1024)) {
|
|
GLvoid *out = pixels;
|
|
pixel_quarterscale(pixels, &out, width, height, format, type);
|
|
if (out != pixels && pixels!=datab)
|
|
free(pixels);
|
|
pixels = out;
|
|
width /= 4;
|
|
height /= 4;
|
|
bound->shrink = 2;
|
|
} else {
|
|
GLvoid *out = pixels;
|
|
pixel_halfscale(pixels, &out, width, height, format, type);
|
|
if (out != pixels && pixels!=datab)
|
|
free(pixels);
|
|
pixels = out;
|
|
width /= 2;
|
|
height /= 2;
|
|
bound->shrink = 1;
|
|
}
|
|
}
|
|
}
|
|
if (bound && (texshrink==5)) {
|
|
while (((width%2==0) && (height%2==0)) &&
|
|
((width > 256) && (height > 8)) || ((height > 256) && (width > 8))) {
|
|
GLvoid *out = pixels;
|
|
pixel_halfscale(pixels, &out, width, height, format, type);
|
|
if (out != pixels && pixels!=datab)
|
|
free(pixels);
|
|
pixels = out;
|
|
width /= 2;
|
|
height /= 2;
|
|
bound->shrink++;
|
|
}
|
|
}
|
|
if (bound && (texshrink==6)) {
|
|
if (((width%2==0) && (height%2==0)) &&
|
|
((width > 128) && (height > 8)) || ((height > 128) && (width > 8))) {
|
|
if (((width%2==0) && (height%2==0)) && (width>=512) || (height>=512)) {
|
|
while (((width > 256) && (height > 8)) || ((height > 256) && (width > 8))) {
|
|
GLvoid *out = pixels;
|
|
pixel_halfscale(pixels, &out, width, height, format, type);
|
|
if (out != pixels && pixels!=datab)
|
|
free(pixels);
|
|
pixels = out;
|
|
width /= 2;
|
|
height /= 2;
|
|
bound->shrink++;
|
|
}
|
|
} else {
|
|
GLvoid *out = pixels;
|
|
pixel_halfscale(pixels, &out, width, height, format, type);
|
|
if (out != pixels && pixels!=datab)
|
|
free(pixels);
|
|
pixels = out;
|
|
width /= 2;
|
|
height /= 2;
|
|
bound->shrink = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (texdump) {
|
|
if (bound) {
|
|
pixel_to_ppm(pixels, width, height, format, type, bound->texture);
|
|
}
|
|
}
|
|
} else {
|
|
if (texstream && bound && (target==GL_TEXTURE_2D) && (width>=256 && height>=256) &&
|
|
((internalformat==GL_RGB) || (internalformat==3) || (internalformat==GL_RGB8) || (internalformat==GL_BGR) || (internalformat==GL_RGB5)) || (texstream==2) ) {
|
|
bound->streamingID = AddStreamed(width, height, bound->texture);
|
|
if (bound->streamingID>-1) { // success
|
|
bound->streamed = true;
|
|
ApplyFilterID(bound->streamingID, bound->min_filter, bound->mag_filter);
|
|
GLboolean tmp = state.enable.texture_2d[state.texture.active];
|
|
LOAD_GLES(glDisable);
|
|
LOAD_GLES(glEnable);
|
|
if (tmp)
|
|
gles_glDisable(GL_TEXTURE_2D);
|
|
ActivateStreaming(bound->streamingID); //Activate the newly created texture
|
|
format = GL_RGB;
|
|
type = GL_UNSIGNED_SHORT_5_6_5;
|
|
if (tmp)
|
|
gles_glEnable(GL_TEXTURE_STREAM_IMG);
|
|
}
|
|
}
|
|
if (bound) {
|
|
bound->shrink = 0;
|
|
if (!bound->streamed)
|
|
swizzle_texture(width, height, &format, &type, NULL); // convert format even if data is NULL
|
|
if ((texshrink>0) && !bound->streamed)
|
|
if ((texshrink==1) ||
|
|
((texshrink>1) &&
|
|
((width > ((texshrink==2)?512:256)) && (height > 8))
|
|
|| ((height > ((texshrink==2)?512:256)) && (width > 8)))) {
|
|
if ((texshrink==4) && ((width>=1024) || (height>=1024))) {
|
|
width /= 4;
|
|
height /= 4;
|
|
bound->shrink = 2;
|
|
} else {
|
|
width /= 2;
|
|
height /= 2;
|
|
bound->shrink = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TODO:
|
|
GL_INVALID_VALUE is generated if border is not 0.
|
|
GL_INVALID_OPERATION is generated if type is
|
|
GL_UNSIGNED_SHORT_5_6_5 and format is not GL_RGB.
|
|
|
|
GL_INVALID_OPERATION is generated if type is one of
|
|
GL_UNSIGNED_SHORT_4_4_4_4, or GL_UNSIGNED_SHORT_5_5_5_1
|
|
and format is not GL_RGBA.
|
|
*/
|
|
|
|
LOAD_GLES(glTexImage2D);
|
|
LOAD_GLES(glTexSubImage2D);
|
|
LOAD_GLES(glTexParameteri);
|
|
|
|
switch (target) {
|
|
case GL_PROXY_TEXTURE_2D:
|
|
break;
|
|
default: {
|
|
GLsizei nheight = npot(height), nwidth = npot(width);
|
|
if (texstream && bound && bound->streamed) {
|
|
nwidth = width;
|
|
nheight = height;
|
|
}
|
|
if (bound && (level == 0)) {
|
|
bound->width = width;
|
|
bound->height = height;
|
|
bound->nwidth = nwidth;
|
|
bound->nheight = nheight;
|
|
bound->format = format;
|
|
bound->type = type;
|
|
}
|
|
if (!(texstream && bound && bound->streamed)) {
|
|
if (bound && bound->mipmap_need && !bound->mipmap_auto && (automipmap!=3))
|
|
gles_glTexParameteri( target, GL_GENERATE_MIPMAP, GL_TRUE );
|
|
if (height != nheight || width != nwidth) {
|
|
gles_glTexImage2D(target, level, format, nwidth, nheight, border,
|
|
format, type, NULL);
|
|
if (pixels) gles_glTexSubImage2D(target, level, 0, 0, width, height,
|
|
format, type, pixels);
|
|
errorGL();
|
|
} else {
|
|
gles_glTexImage2D(target, level, format, width, height, border,
|
|
format, type, pixels);
|
|
errorGL();
|
|
}
|
|
if (bound && bound->mipmap_need && !bound->mipmap_auto && (automipmap!=3))
|
|
gles_glTexParameteri( target, GL_GENERATE_MIPMAP, GL_FALSE );
|
|
} else {
|
|
if (pixels)
|
|
glTexSubImage2D(target, level, 0, 0, width, height, format, type, pixels); // (should never happens) updload the 1st data...
|
|
}
|
|
}
|
|
}
|
|
if ((target==GL_TEXTURE_2D) && texcopydata && bound && ((texstream && !bound->streamed) || !texstream)) {
|
|
if (bound->data)
|
|
bound->data=realloc(bound->data, width*height*4);
|
|
else
|
|
bound->data = malloc(width*height*4);
|
|
if (datab) {
|
|
if (!pixel_convert(pixels, &bound->data, width, height, format, type, GL_RGBA, GL_UNSIGNED_BYTE, 0))
|
|
printf("LIBGL: Error on pixel_convert when TEXCOPY in glTexImage2D\n");
|
|
} else {
|
|
//memset(bound->data, 0, width*height*4);
|
|
}
|
|
}
|
|
if (pixels != datab) {
|
|
free(pixels);
|
|
}
|
|
}
|
|
|
|
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
|
GLsizei width, GLsizei height, GLenum format, GLenum type,
|
|
const GLvoid *data) {
|
|
|
|
//PUSH_IF_COMPILING(glTexSubImage2D);
|
|
if (state.gl_batch) flush();
|
|
|
|
GLvoid *datab = (GLvoid*)data;
|
|
if (state.buffers.unpack)
|
|
datab += (uintptr_t)state.buffers.pack->data;
|
|
GLvoid *pixels = (GLvoid*)datab;
|
|
|
|
LOAD_GLES(glTexSubImage2D);
|
|
LOAD_GLES(glTexParameteri);
|
|
noerrorShim();
|
|
//printf("glTexSubImage2D on target=0x%04X with unpack_row_length(%i), size(%i,%i), pos(%i,%i) and skip={%i,%i}, format=%04x, type=%04x, level=%i, texture=%u\n", target, state.texture.unpack_row_length, width, height, xoffset, yoffset, state.texture.unpack_skip_pixels, state.texture.unpack_skip_rows, format, type, level, state.texture.bound[state.texture.active]->texture);
|
|
if (width==0 || height==0)
|
|
return;
|
|
target = map_tex_target(target);
|
|
|
|
gltexture_t *bound = state.texture.bound[state.texture.active];
|
|
if (automipmap) {
|
|
if (bound && (level>0))
|
|
if ((automipmap==1) || (automipmap==3) || bound->mipmap_need)
|
|
return; // has been handled by auto_mipmap
|
|
else
|
|
bound->mipmap_need = 1;
|
|
}
|
|
|
|
if ((state.texture.unpack_row_length && state.texture.unpack_row_length != width) || state.texture.unpack_skip_pixels || state.texture.unpack_skip_rows) {
|
|
int imgWidth, pixelSize;
|
|
pixelSize = pixel_sizeof(format, type);
|
|
imgWidth = ((state.texture.unpack_row_length)? state.texture.unpack_row_length:width) * pixelSize;
|
|
GLubyte *dst = (GLubyte *)malloc(width * height * pixelSize);
|
|
pixels = (GLvoid *)dst;
|
|
const GLubyte *src = (GLubyte *)datab;
|
|
src += state.texture.unpack_skip_pixels * pixelSize + state.texture.unpack_skip_rows * imgWidth;
|
|
for (int y = 0; y < height; y += 1) {
|
|
memcpy(dst, src, width * pixelSize);
|
|
src += imgWidth;
|
|
dst += width * pixelSize;
|
|
}
|
|
}
|
|
|
|
GLvoid *old = pixels;
|
|
if (bound && texstream && (bound->streamed)) {
|
|
// Optimisation, let's do convert directly to the right place...
|
|
GLvoid *tmp = GetStreamingBuffer(bound->streamingID);
|
|
tmp += (yoffset*bound->width+xoffset)*2;
|
|
if (! pixel_convert(old, &tmp, width, height,
|
|
format, type, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, bound->width)) {
|
|
printf("libGL swizzle error: (%#4x, %#4x -> GL_RGB, UNSIGNED_SHORT_5_6_5)\n",
|
|
format, type);
|
|
}
|
|
format = GL_RGB;
|
|
type = GL_UNSIGNED_SHORT_5_6_5;
|
|
} else {
|
|
//pixels = (GLvoid *)swizzle_texture(width, height, &format, &type, old);
|
|
if (!pixel_convert(old, &pixels, width, height, format, type, bound->format, bound->type, 0)) {
|
|
printf("LIBGL: Error in pixel_convert while glTexSubImage2D\n");
|
|
} else {
|
|
format = bound->format;
|
|
type = bound->type;
|
|
}
|
|
|
|
}
|
|
if (old != pixels && old != datab)
|
|
free(old);
|
|
|
|
if (bound->shrink) {
|
|
// special case for width/height == 1
|
|
if (width==1)
|
|
width+=(xoffset%2);
|
|
if (height==1)
|
|
height+=(yoffset%2);
|
|
if ((width==1) || (height==1)) {
|
|
// nothing to do...
|
|
if (pixels != datab)
|
|
free((GLvoid *)pixels);
|
|
return;
|
|
}
|
|
// ok, now standard cases....
|
|
xoffset /= 2*bound->shrink;
|
|
yoffset /= 2*bound->shrink;
|
|
old = pixels;
|
|
if (bound->shrink==1)
|
|
pixel_halfscale(pixels, &old, width, height, format, type);
|
|
else
|
|
pixel_quarterscale(pixels, &old, width, height, format, type);
|
|
if (old != pixels && pixels!=data)
|
|
free(pixels);
|
|
pixels = old;
|
|
width /= 2*bound->shrink;
|
|
height /= 2*bound->shrink;
|
|
}
|
|
|
|
if (texdump) {
|
|
if (bound) {
|
|
pixel_to_ppm(pixels, width, height, format, type, bound->texture);
|
|
}
|
|
}
|
|
|
|
if (bound && bound->mipmap_need && !bound->mipmap_auto && (automipmap!=3) && (!texstream || (texstream && !bound->streamed)))
|
|
gles_glTexParameteri( target, GL_GENERATE_MIPMAP, GL_TRUE );
|
|
|
|
if (bound && texstream && bound->streamed) {
|
|
/* // copy the texture to the buffer
|
|
void* tmp = GetStreamingBuffer(bound->streamingID);
|
|
for (int yy=0; yy<height; yy++) {
|
|
memcpy(tmp+((yy+yoffset)*bound->width+xoffset)*2, pixels+(yy*width)*2, width*2);
|
|
}*/
|
|
} else {
|
|
gles_glTexSubImage2D(target, level, xoffset, yoffset,
|
|
width, height, format, type, pixels);
|
|
errorGL();
|
|
}
|
|
|
|
if (bound && bound->mipmap_need && !bound->mipmap_auto && (automipmap!=3) && (!texstream || (texstream && !bound->streamed)))
|
|
gles_glTexParameteri( target, GL_GENERATE_MIPMAP, GL_FALSE );
|
|
|
|
if ((target==GL_TEXTURE_2D) && texcopydata && bound && ((texstream && !bound->streamed) || !texstream)) {
|
|
//printf("*texcopy* glTexSubImage2D, xy=%i,%i, size=%i,%i, format=0x%04X, type=0x%04X, tex=%u\n", xoffset, yoffset, width, height, format, type, bound->glname);
|
|
GLvoid * tmp = bound->data;
|
|
tmp += (yoffset*bound->width + xoffset)*4;
|
|
if (!pixel_convert(pixels, &tmp, width, height, format, type, GL_RGBA, GL_UNSIGNED_BYTE, bound->width))
|
|
printf("LIBGL: Error on pixel_convert while TEXCOPY in glTexSubImage2D\n");
|
|
}
|
|
|
|
if (pixels != datab)
|
|
free((GLvoid *)pixels);
|
|
}
|
|
|
|
// 1d stubs
|
|
void glTexImage1D(GLenum target, GLint level, GLint internalFormat,
|
|
GLsizei width, GLint border,
|
|
GLenum format, GLenum type, const GLvoid *data) {
|
|
|
|
// TODO: maybe too naive to force GL_TEXTURE_2D here?
|
|
glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, 1,
|
|
border, format, type, data);
|
|
}
|
|
void glTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
|
GLsizei width, GLenum format, GLenum type,
|
|
const GLvoid *data) {
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, level, xoffset, yoffset,
|
|
width, 1, format, type, data);
|
|
}
|
|
void glCopyTexImage1D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y,
|
|
GLsizei width, GLint border) {
|
|
glCopyTexImage2D(GL_TEXTURE_2D, level, internalformat, x, y, width, 1, border);
|
|
|
|
}
|
|
|
|
void glCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y,
|
|
GLsizei width) {
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, level, xoffset, 0, x, y, width, 1);
|
|
}
|
|
|
|
|
|
|
|
// 3d stubs
|
|
void glTexImage3D(GLenum target, GLint level, GLint internalFormat,
|
|
GLsizei width, GLsizei height, GLsizei depth, GLint border,
|
|
GLenum format, GLenum type, const GLvoid *data) {
|
|
|
|
// TODO: maybe too naive to force GL_TEXTURE_2D here?
|
|
glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height,
|
|
border, format, type, data);
|
|
}
|
|
void glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
|
GLsizei width, GLsizei height, GLsizei depth, GLenum format,
|
|
GLenum type, const GLvoid *data) {
|
|
|
|
glTexSubImage2D(target, level, xoffset, yoffset,
|
|
width, height, format, type, data);
|
|
}
|
|
|
|
void glPixelStorei(GLenum pname, GLint param) {
|
|
// TODO: add to glGetIntegerv?
|
|
LOAD_GLES(glPixelStorei);
|
|
noerrorShim();
|
|
switch (pname) {
|
|
case GL_UNPACK_ROW_LENGTH:
|
|
state.texture.unpack_row_length = param;
|
|
break;
|
|
case GL_UNPACK_SKIP_PIXELS:
|
|
state.texture.unpack_skip_pixels = param;
|
|
break;
|
|
case GL_UNPACK_SKIP_ROWS:
|
|
state.texture.unpack_skip_rows = param;
|
|
break;
|
|
case GL_UNPACK_LSB_FIRST:
|
|
state.texture.unpack_lsb_first = param;
|
|
break;
|
|
case GL_PACK_ROW_LENGTH:
|
|
state.texture.pack_row_length = param;
|
|
break;
|
|
case GL_PACK_SKIP_PIXELS:
|
|
state.texture.pack_skip_pixels = param;
|
|
break;
|
|
case GL_PACK_SKIP_ROWS:
|
|
state.texture.pack_skip_rows = param;
|
|
break;
|
|
case GL_PACK_LSB_FIRST:
|
|
state.texture.pack_lsb_first = param;
|
|
break;
|
|
default:
|
|
errorGL();
|
|
gles_glPixelStorei(pname, param);
|
|
break;
|
|
}
|
|
}
|
|
GLboolean glIsTexture( GLuint texture) {
|
|
noerrorShim();
|
|
if (!texture) {
|
|
return GL_FALSE;
|
|
}
|
|
int ret;
|
|
khint_t k;
|
|
khash_t(tex) *list = state.texture.list;
|
|
if (! list) {
|
|
return GL_FALSE;
|
|
}
|
|
k = kh_get(tex, list, texture);
|
|
gltexture_t *tex = NULL;
|
|
if (k == kh_end(list)) {
|
|
return GL_FALSE;
|
|
}
|
|
return GL_TRUE;
|
|
}
|
|
|
|
void glBindTexture(GLenum target, GLuint texture) {
|
|
noerrorShim();
|
|
if ((target!=GL_PROXY_TEXTURE_2D) && ((state.list.compiling || state.gl_batch) && state.list.active)) {
|
|
// check if already a texture binded, if yes, create a new list
|
|
NewStage(state.list.active, STAGE_BINDTEX);
|
|
rlBindTexture(state.list.active, target, texture);
|
|
} else {
|
|
int tex_changed = 1;
|
|
int streamingID = -1;
|
|
gltexture_t *tex = NULL;
|
|
//printf("glBindTexture(0x%04X, %u), active=%i, client=%i\n", target, texture, state.texture.active, state.texture.client);
|
|
if (texture) {
|
|
int ret;
|
|
khint_t k;
|
|
khash_t(tex) *list = state.texture.list;
|
|
if (! list) {
|
|
list = state.texture.list = kh_init(tex);
|
|
// segfaults if we don't do a single put
|
|
kh_put(tex, list, 1, &ret);
|
|
kh_del(tex, list, 1);
|
|
}
|
|
k = kh_get(tex, list, texture);
|
|
|
|
if (k == kh_end(list)){
|
|
LOAD_GLES(glGenTextures);
|
|
k = kh_put(tex, list, texture, &ret);
|
|
tex = kh_value(list, k) = malloc(sizeof(gltexture_t));
|
|
tex->texture = texture;
|
|
gles_glGenTextures(1, &tex->glname);
|
|
tex->target = target;
|
|
tex->width = 0;
|
|
tex->height = 0;
|
|
tex->uploaded = false;
|
|
tex->mipmap_auto = 0;
|
|
tex->mipmap_need = 0;
|
|
tex->alpha = true;
|
|
tex->streamed = false;
|
|
tex->streamingID = -1;
|
|
tex->min_filter = tex->mag_filter = GL_LINEAR;
|
|
tex->format = GL_RGBA;
|
|
tex->type = GL_UNSIGNED_BYTE;
|
|
tex->data = NULL;
|
|
} else {
|
|
tex = kh_value(list, k);
|
|
}
|
|
if (state.texture.bound[state.texture.active] == tex)
|
|
tex_changed = 0;
|
|
texture = tex->glname;
|
|
if (texstream && tex->streamed)
|
|
streamingID = tex->streamingID;
|
|
} else {
|
|
if (state.texture.bound[state.texture.active] == NULL)
|
|
tex_changed = 0;
|
|
}
|
|
|
|
LOAD_GLES(glDisable);
|
|
LOAD_GLES(glEnable);
|
|
tex_changed=1;
|
|
|
|
if (tex_changed) {
|
|
|
|
GLboolean tmp = state.enable.texture_2d[state.texture.active];
|
|
if (texstream) { // unbind streaming texture if any...
|
|
gltexture_t *bound = state.texture.bound[state.texture.active];
|
|
if (bound && bound->streamed) {
|
|
if (tmp)
|
|
gles_glDisable(GL_TEXTURE_STREAM_IMG);
|
|
DeactivateStreaming();
|
|
if (tmp)
|
|
gles_glEnable(GL_TEXTURE_2D);
|
|
}
|
|
}
|
|
|
|
state.texture.rect_arb[state.texture.active] = (target == GL_TEXTURE_RECTANGLE_ARB);
|
|
target = map_tex_target(target);
|
|
|
|
state.texture.bound[state.texture.active] = tex;
|
|
|
|
LOAD_GLES(glBindTexture);
|
|
if (texstream && (streamingID>-1)) {
|
|
if (tmp)
|
|
gles_glDisable(GL_TEXTURE_2D);
|
|
ActivateStreaming(streamingID);
|
|
if (tmp)
|
|
gles_glEnable(GL_TEXTURE_STREAM_IMG);
|
|
} else {
|
|
gles_glBindTexture(target, texture);
|
|
errorGL();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: also glTexParameterf(v)?
|
|
void glTexParameteri(GLenum target, GLenum pname, GLint param) {
|
|
PUSH_IF_COMPILING(glTexParameteri);
|
|
LOAD_GLES(glTexParameteri);
|
|
target = map_tex_target(target);
|
|
gltexture_t *texture = state.texture.bound[state.texture.active];
|
|
switch (pname) {
|
|
case GL_TEXTURE_MIN_FILTER:
|
|
case GL_TEXTURE_MAG_FILTER:
|
|
switch (param) {
|
|
case GL_NEAREST_MIPMAP_NEAREST:
|
|
case GL_NEAREST_MIPMAP_LINEAR:
|
|
case GL_LINEAR_MIPMAP_NEAREST:
|
|
case GL_LINEAR_MIPMAP_LINEAR:
|
|
if (texture)
|
|
texture->mipmap_need = true;
|
|
if (automipmap==3)
|
|
switch (param) {
|
|
case GL_NEAREST_MIPMAP_NEAREST:
|
|
case GL_NEAREST_MIPMAP_LINEAR:
|
|
param = GL_NEAREST;
|
|
break;
|
|
case GL_LINEAR_MIPMAP_NEAREST:
|
|
case GL_LINEAR_MIPMAP_LINEAR:
|
|
param = GL_LINEAR;
|
|
break;
|
|
}
|
|
if (pname==GL_TEXTURE_MIN_FILTER) if (texture) texture->min_filter = param;
|
|
if (pname==GL_TEXTURE_MAG_FILTER) if (texture) texture->mag_filter = param;
|
|
break;
|
|
}
|
|
case GL_TEXTURE_WRAP_S:
|
|
case GL_TEXTURE_WRAP_T:
|
|
switch (param) {
|
|
case GL_CLAMP:
|
|
param = GL_CLAMP_TO_EDGE;
|
|
break;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MAX_LEVEL:
|
|
if (texture)
|
|
texture->mipmap_auto = (param)?1:0;
|
|
return; // not on GLES
|
|
case GL_TEXTURE_MIN_LOD:
|
|
case GL_TEXTURE_MAX_LOD:
|
|
case GL_TEXTURE_LOD_BIAS:
|
|
return; // not on GLES
|
|
case GL_GENERATE_MIPMAP:
|
|
if (texture)
|
|
texture->mipmap_auto = (param)?1:0;
|
|
break;
|
|
}
|
|
gles_glTexParameteri(target, pname, param);
|
|
errorGL();
|
|
}
|
|
|
|
void glTexParameterf(GLenum target, GLenum pname, GLfloat param) {
|
|
glTexParameteri(target, pname, param);
|
|
}
|
|
|
|
void glDeleteTextures(GLsizei n, const GLuint *textures) {
|
|
if (state.gl_batch) flush();
|
|
noerrorShim();
|
|
LOAD_GLES(glDeleteTextures);
|
|
khash_t(tex) *list = state.texture.list;
|
|
if (list) {
|
|
khint_t k;
|
|
gltexture_t *tex;
|
|
for (int i = 0; i < n; i++) {
|
|
GLuint t = textures[i];
|
|
k = kh_get(tex, list, t);
|
|
if (k != kh_end(list)) {
|
|
tex = kh_value(list, k);
|
|
int a;
|
|
for (a=0; a<MAX_TEX; a++) {
|
|
if (tex == state.texture.bound[a])
|
|
state.texture.bound[a] = NULL;
|
|
}
|
|
gles_glDeleteTextures(1, &tex->glname);
|
|
errorGL();
|
|
if (texstream && tex->streamed)
|
|
FreeStreamed(tex->streamingID);
|
|
#if 1
|
|
kh_del(tex, list, k);
|
|
if (tex->data) free(tex->data);
|
|
free(tex);
|
|
#else
|
|
tex->glname = tex->texture;
|
|
tex->streamed = false;
|
|
tex->streamingID = -1;
|
|
if (tex->data) free(tex->data);
|
|
tex->data = NULL;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void glGenTextures(GLsizei n, GLuint * textures) {
|
|
if (n<=0)
|
|
return;
|
|
if (state.gl_batch) flush();
|
|
LOAD_GLES(glGenTextures);
|
|
gles_glGenTextures(n, textures);
|
|
errorGL();
|
|
// now, add all the textures to the list
|
|
int ret;
|
|
khint_t k;
|
|
khash_t(tex) *list = state.texture.list;
|
|
if (! list) {
|
|
list = state.texture.list = kh_init(tex);
|
|
// segfaults if we don't do a single put
|
|
kh_put(tex, list, 1, &ret);
|
|
kh_del(tex, list, 1);
|
|
}
|
|
|
|
for (int i=0; i<n; i++) {
|
|
k = kh_get(tex, list, textures[i]);
|
|
gltexture_t *tex = NULL;
|
|
if (k == kh_end(list)){
|
|
k = kh_put(tex, list, textures[i], &ret);
|
|
tex = kh_value(list, k) = malloc(sizeof(gltexture_t));
|
|
tex->texture = textures[i];
|
|
tex->glname = textures[i];
|
|
tex->width = 0;
|
|
tex->height = 0;
|
|
tex->uploaded = false;
|
|
tex->mipmap_auto = 0;
|
|
tex->mipmap_need = 0;
|
|
tex->streamingID = -1;
|
|
tex->streamed = false;
|
|
tex->min_filter = tex->mag_filter = GL_NEAREST;
|
|
tex->format = GL_RGBA;
|
|
tex->type = GL_UNSIGNED_BYTE;
|
|
tex->data = NULL;
|
|
} else {
|
|
tex = kh_value(list, k);
|
|
// in case of no delete here...
|
|
if (tex->glname==0)
|
|
tex->glname = tex->texture;
|
|
}
|
|
}
|
|
}
|
|
|
|
GLboolean glAreTexturesResident(GLsizei n, const GLuint *textures, GLboolean *residences) {
|
|
noerrorShim();
|
|
return true;
|
|
}
|
|
|
|
void glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params) {
|
|
// simplification: not taking "target" into account here
|
|
if (state.gl_batch) flush();
|
|
*params = 0;
|
|
noerrorShim();
|
|
gltexture_t* bound = state.texture.bound[state.texture.active];
|
|
switch (pname) {
|
|
case GL_TEXTURE_WIDTH:
|
|
if (target==GL_PROXY_TEXTURE_2D)
|
|
(*params) = proxy_width>>level;
|
|
else
|
|
(*params) = ((bound)?bound->width:2048)>>level;
|
|
break;
|
|
case GL_TEXTURE_HEIGHT:
|
|
if (target==GL_PROXY_TEXTURE_2D)
|
|
(*params) = proxy_height>>level;
|
|
else
|
|
(*params) = ((bound)?bound->height:2048)>>level;
|
|
break;
|
|
case GL_TEXTURE_INTERNAL_FORMAT:
|
|
(*params) = GL_RGBA;
|
|
break;
|
|
case GL_TEXTURE_DEPTH:
|
|
(*params) = 0;
|
|
break;
|
|
case GL_TEXTURE_RED_TYPE:
|
|
case GL_TEXTURE_GREEN_TYPE:
|
|
case GL_TEXTURE_BLUE_TYPE:
|
|
case GL_TEXTURE_ALPHA_TYPE:
|
|
case GL_TEXTURE_DEPTH_TYPE:
|
|
(*params) = GL_FLOAT;
|
|
break;
|
|
case GL_TEXTURE_RED_SIZE:
|
|
case GL_TEXTURE_GREEN_SIZE:
|
|
case GL_TEXTURE_BLUE_SIZE:
|
|
case GL_TEXTURE_ALPHA_SIZE:
|
|
(*params) = 8;
|
|
break;
|
|
case GL_TEXTURE_DEPTH_SIZE:
|
|
(*params) = 0;
|
|
break;
|
|
case GL_TEXTURE_COMPRESSED:
|
|
(*params) = GL_FALSE;
|
|
break;
|
|
case GL_TEXTURE_COMPRESSED_IMAGE_SIZE:
|
|
(*params) = (bound)?(bound->width*bound->height*4):0;
|
|
break;
|
|
default:
|
|
errorShim(GL_INVALID_ENUM); //Wrong here...
|
|
printf("Stubbed glGetTexLevelParameteriv(%04x, %i, %04x, %p)\n", target, level, pname, params);
|
|
}
|
|
}
|
|
|
|
extern GLuint current_fb; // from framebuffers.c
|
|
|
|
void glGetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid * img) {
|
|
if (state.gl_batch) flush();
|
|
if (state.texture.bound[state.texture.active]==NULL)
|
|
return; // no texture bounded...
|
|
if (level != 0) {
|
|
//TODO
|
|
printf("STUBBED glGetTexImage with level=%i\n", level);
|
|
return;
|
|
}
|
|
|
|
if (target!=GL_TEXTURE_2D)
|
|
return;
|
|
|
|
gltexture_t* bound = state.texture.bound[state.texture.active];
|
|
int width = bound->width;
|
|
int height = bound->height;
|
|
//printf("glGetTexImage(0x%04X, %i, 0x%04X, 0x%04X, 0x%p), texture=%u, size=%i,%i\n", target, level, format, type, img, bound->glname, width, height);
|
|
|
|
GLvoid *dst = img;
|
|
if (state.buffers.pack)
|
|
dst += (uintptr_t)state.buffers.pack->data;
|
|
|
|
if (texstream && bound->streamed) {
|
|
noerrorShim();
|
|
pixel_convert(GetStreamingBuffer(bound->streamingID), &dst, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, format, type, 0);
|
|
readfboEnd();
|
|
return;
|
|
}
|
|
|
|
if (texcopydata && bound->data) {
|
|
errorShim(GL_INVALID_ENUM);
|
|
if (!pixel_convert(bound->data, &dst, width, height, GL_RGBA, GL_UNSIGNED_BYTE, format, type, 0))
|
|
printf("LIBGL: Error on pixel_convert while glGetTexImage\n");
|
|
} else {
|
|
// Setup an FBO the same size of the texture
|
|
GLuint oldBind = bound->glname;
|
|
GLuint old_fbo = current_fb;
|
|
GLuint fbo;
|
|
|
|
glGenFramebuffers(1, &fbo);
|
|
glBindFramebuffer(GL_FRAMEBUFFER_OES, fbo);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, oldBind, 0);
|
|
// Read the pixels!
|
|
glReadPixels(0, 0, width, height, format, type, img); // using "full" version with conversion of format/type
|
|
glBindFramebuffer(GL_FRAMEBUFFER_OES, old_fbo);
|
|
glDeleteFramebuffers(1, &fbo);
|
|
noerrorShim();
|
|
}
|
|
}
|
|
|
|
void glActiveTexture( GLenum texture ) {
|
|
PUSH_IF_COMPILING(glActiveTexture);
|
|
|
|
if ((texture < GL_TEXTURE0) || (texture >= GL_TEXTURE0+MAX_TEX)) {
|
|
errorShim(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
state.texture.active = texture - GL_TEXTURE0;
|
|
LOAD_GLES(glActiveTexture);
|
|
gles_glActiveTexture(texture);
|
|
errorGL();
|
|
}
|
|
|
|
void glClientActiveTexture( GLenum texture ) {
|
|
if ((texture < GL_TEXTURE0) || (texture >= GL_TEXTURE0+MAX_TEX)) {
|
|
errorShim(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
state.texture.client = texture - GL_TEXTURE0;
|
|
LOAD_GLES(glClientActiveTexture);
|
|
gles_glClientActiveTexture(texture);
|
|
errorGL();
|
|
}
|
|
|
|
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * data) {
|
|
//printf("glReadPixels(%i, %i, %i, %i, 0x%04X, 0x%04X, 0x%p)\n", x, y, width, height, format, type, data);
|
|
GLuint old_glbatch = state.gl_batch;
|
|
if (state.gl_batch) {
|
|
flush();
|
|
state.gl_batch = 0;
|
|
}
|
|
if (state.list.compiling && state.list.active) {
|
|
errorShim(GL_INVALID_OPERATION);
|
|
return; // never in list
|
|
}
|
|
LOAD_GLES(glReadPixels);
|
|
errorGL();
|
|
GLvoid* dst = data;
|
|
if (state.buffers.pack)
|
|
dst += (uintptr_t)state.buffers.pack->data;
|
|
|
|
readfboBegin();
|
|
if (format == GL_RGBA && format == GL_UNSIGNED_BYTE) {
|
|
// easy passthru
|
|
gles_glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, dst);
|
|
readfboEnd();
|
|
state.gl_batch = old_glbatch;
|
|
return;
|
|
}
|
|
// grab data in GL_RGBA format
|
|
GLvoid *pixels = malloc(width*height*4);
|
|
gles_glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
if (! pixel_convert(pixels, &dst, width, height,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, format, type, 0)) {
|
|
printf("libGL ReadPixels error: (GL_RGBA, UNSIGNED_BYTE -> %#4x, %#4x )\n",
|
|
format, type);
|
|
}
|
|
free(pixels);
|
|
readfboEnd();
|
|
state.gl_batch = old_glbatch;
|
|
return;
|
|
}
|
|
|
|
void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
|
GLint x, GLint y, GLsizei width, GLsizei height) {
|
|
//printf("glCopyTexSubImage2D(%i, %i, %i, %i, %i, %i, %i, %i), bounded texture=%u format/type=0x%04X, 0x%04X\n", target, level, xoffset, yoffset, x, y, width, height, (state.texture.bound[state.texture.active])?state.texture.bound[state.texture.active]->texture:0, (state.texture.bound[state.texture.active])?state.texture.bound[state.texture.active]->format:0, (state.texture.bound[state.texture.active])?state.texture.bound[state.texture.active]->type:0);
|
|
// PUSH_IF_COMPILING(glCopyTexSubImage2D);
|
|
if (state.gl_batch) flush();
|
|
|
|
LOAD_GLES(glCopyTexSubImage2D);
|
|
errorGL();
|
|
|
|
// "Unmap" if buffer mapped...
|
|
glbuffer_t *pack = state.buffers.pack;
|
|
glbuffer_t *unpack = state.buffers.unpack;
|
|
state.buffers.pack = NULL;
|
|
state.buffers.unpack = NULL;
|
|
|
|
gltexture_t* bound = state.texture.bound[state.texture.active];
|
|
if (!bound) {
|
|
errorShim(GL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
if (bound && bound->streamed) {
|
|
void* buff = GetStreamingBuffer(bound->streamingID);
|
|
if ((bound->width == width) && (bound->height == height) && (xoffset == yoffset == 0)) {
|
|
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, buff);
|
|
} else {
|
|
void* tmp = malloc(width*height*2);
|
|
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, tmp);
|
|
for (int y=0; y<height; y++) {
|
|
memcpy(buff+((yoffset+y)*bound->width+xoffset)*2, tmp+y*width*2, width*2);
|
|
}
|
|
free(tmp);
|
|
}
|
|
} else {
|
|
void* tmp = malloc(width*height*4);
|
|
GLenum format = (bound)?bound->format:GL_RGBA;
|
|
GLenum type = (bound)?bound->type:GL_UNSIGNED_BYTE;
|
|
glReadPixels(x, y, width, height, format, type, tmp);
|
|
glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, tmp);
|
|
free(tmp);
|
|
}
|
|
// "Remap" if buffer mapped...
|
|
state.buffers.pack = pack;
|
|
state.buffers.unpack = unpack;
|
|
}
|
|
|
|
|
|
void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y,
|
|
GLsizei width, GLsizei height, GLint border) {
|
|
//printf("glCopyTexImage2D(0x%04X, %i, 0x%04X, %i, %i, %i, %i, %i), current_fb=%u\n", target, level, internalformat, x, y, width, height, border, current_fb);
|
|
//PUSH_IF_COMPILING(glCopyTexImage2D);
|
|
GLuint old_glbatch = state.gl_batch;
|
|
if (state.gl_batch) {
|
|
flush();
|
|
state.gl_batch = 0;
|
|
}
|
|
|
|
errorGL();
|
|
|
|
// "Unmap" if buffer mapped...
|
|
glbuffer_t *pack = state.buffers.pack;
|
|
glbuffer_t *unpack = state.buffers.unpack;
|
|
state.buffers.pack = NULL;
|
|
state.buffers.unpack = NULL;
|
|
|
|
void* tmp = malloc(width*height*4);
|
|
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
|
|
glTexImage2D(target, level, internalformat, width, height, border, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
|
|
free(tmp);
|
|
|
|
// "Remap" if buffer mapped...
|
|
state.buffers.pack = pack;
|
|
state.buffers.unpack = unpack;
|
|
|
|
state.gl_batch = old_glbatch;
|
|
}
|
|
|
|
|
|
|
|
GLboolean isDXTc(GLenum format) {
|
|
switch (format) {
|
|
case COMPRESSED_RGB_S3TC_DXT1_EXT:
|
|
case COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
case COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
case COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GLvoid *uncompressDXTc(GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data) {
|
|
// uncompress a DXTc image
|
|
// get pixel size of uncompressed image => fixed RGBA
|
|
int pixelsize = 4;
|
|
/* if (format==COMPRESSED_RGB_S3TC_DXT1_EXT)
|
|
pixelsize = 3;*/
|
|
// check with the size of the input data stream if the stream is in fact uncompressed
|
|
if (imageSize == width*height*pixelsize || data==NULL) {
|
|
// uncompressed stream
|
|
return (GLvoid*)data;
|
|
}
|
|
// alloc memory
|
|
GLvoid *pixels = malloc(((width+3)&~3)*((height+3)&~3)*pixelsize);
|
|
// uncompress loop
|
|
int blocksize;
|
|
switch (format) {
|
|
case COMPRESSED_RGB_S3TC_DXT1_EXT:
|
|
case COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
blocksize = 8;
|
|
break;
|
|
case COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
case COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
blocksize = 16;
|
|
break;
|
|
}
|
|
uintptr_t src = (uintptr_t) data;
|
|
for (int y=0; y<height; y+=4) {
|
|
for (int x=0; x<width; x+=4) {
|
|
switch(format) {
|
|
case COMPRESSED_RGB_S3TC_DXT1_EXT:
|
|
case COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
DecompressBlockDXT1(x, y, width, (uint8_t*)src, pixels);
|
|
break;
|
|
case COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
DecompressBlockDXT3(x, y, width, (uint8_t*)src, pixels);
|
|
break;
|
|
case COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
DecompressBlockDXT5(x, y, width, (uint8_t*)src, pixels);
|
|
break;
|
|
}
|
|
src+=blocksize;
|
|
}
|
|
}
|
|
return pixels;
|
|
}
|
|
|
|
void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat,
|
|
GLsizei width, GLsizei height, GLint border,
|
|
GLsizei imageSize, const GLvoid *data)
|
|
{
|
|
if (target == GL_PROXY_TEXTURE_2D) {
|
|
proxy_width = (width>2048)?0:width;
|
|
proxy_height = (height>2048)?0:height;
|
|
return;
|
|
}
|
|
if (state.gl_batch) flush();
|
|
|
|
if (state.texture.bound[state.texture.active]==NULL) {
|
|
errorShim(GL_INVALID_OPERATION);
|
|
return; // no texture bounded...
|
|
}
|
|
if (level != 0) {
|
|
noerrorShim();
|
|
//TODO
|
|
//printf("STUBBED glCompressedTexImage2D with level=%i\n", level);
|
|
//return;
|
|
}
|
|
//printf("glCompressedTexImage2D on target=0x%04X with size(%i,%i), internalformat=%04x, imagesize=%i, upackbuffer=%p\n", target, width, height, internalformat, imageSize, state.buffers.unpack?state.buffers.unpack->data:0);
|
|
if ((width<=0) || (height<=0)) {
|
|
noerrorShim();
|
|
return; // nothing to do...
|
|
}
|
|
LOAD_GLES(glCompressedTexImage2D);
|
|
errorGL();
|
|
|
|
glbuffer_t *unpack = state.buffers.unpack;
|
|
state.buffers.unpack = NULL;
|
|
GLvoid *datab = (GLvoid*)data;
|
|
if (unpack)
|
|
datab += (uintptr_t)unpack->data;
|
|
|
|
if (isDXTc(internalformat)) {
|
|
GLvoid *pixels, *half;
|
|
int fact = 0;
|
|
if (datab) {
|
|
if (width<4 || height<4) { // can happens :(
|
|
GLvoid *tmp;
|
|
GLsizei nw=width;
|
|
GLsizei nh=height;
|
|
if (nw<4) nw = 4;
|
|
if (nh<4) nh = 4;
|
|
tmp = uncompressDXTc(nw, nh, internalformat, imageSize, datab);
|
|
pixels = malloc(4*width*height);
|
|
// crop
|
|
for (int y=0; y<height; y++)
|
|
memcpy(pixels+y*width*4, tmp+y*nw*4, width*4);
|
|
free(tmp);
|
|
} else {
|
|
pixels = uncompressDXTc(width, height, internalformat, imageSize, datab);
|
|
}
|
|
// automaticaly reduce the pixel size
|
|
half=pixels;
|
|
state.texture.bound[state.texture.active]->alpha = (internalformat==COMPRESSED_RGB_S3TC_DXT1_EXT)?false:true;
|
|
if (pixel_thirdscale(pixels, &half, width, height, GL_RGBA, GL_UNSIGNED_BYTE))
|
|
fact = 1;
|
|
} else {
|
|
half = NULL;
|
|
fact = 1;
|
|
}
|
|
int oldalign;
|
|
glGetIntegerv(GL_UNPACK_ALIGNMENT, &oldalign);
|
|
if (oldalign!=1)
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glTexImage2D(target, level, GL_RGBA, width>>fact, height>>fact, border, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, half);
|
|
if (oldalign!=1)
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, oldalign);
|
|
if (half!=pixels)
|
|
free(half);
|
|
if (pixels!=datab)
|
|
free(pixels);
|
|
} else {
|
|
state.texture.bound[state.texture.active]->alpha = true;
|
|
state.texture.bound[state.texture.active]->format = internalformat;
|
|
state.texture.bound[state.texture.active]->type = GL_UNSIGNED_BYTE;
|
|
gles_glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, datab);
|
|
}
|
|
state.buffers.unpack = unpack;
|
|
}
|
|
|
|
void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
|
GLsizei width, GLsizei height, GLenum format,
|
|
GLsizei imageSize, const GLvoid *data)
|
|
{
|
|
if (state.gl_batch) flush();
|
|
|
|
if (state.texture.bound[state.texture.active]==NULL) {
|
|
errorShim(GL_INVALID_OPERATION);
|
|
return; // no texture bounded...
|
|
}
|
|
if (level != 0) {
|
|
noerrorShim();
|
|
//TODO
|
|
//printf("STUBBED glCompressedTexSubImage2D with level=%i\n", level);
|
|
return;
|
|
}
|
|
//printf("glCompressedTexSubImage2D with unpack_row_length(%i), size(%i,%i), pos(%i,%i) and skip={%i,%i}, internalformat=%04x, imagesize=%i\n", state.texture.unpack_row_length, width, height, xoffset, yoffset, state.texture.unpack_skip_pixels, state.texture.unpack_skip_rows, format, imageSize);
|
|
glbuffer_t *unpack = state.buffers.unpack;
|
|
state.buffers.unpack = NULL;
|
|
GLvoid *datab = (GLvoid*)data;
|
|
if (unpack)
|
|
datab += (uintptr_t)unpack->data;
|
|
LOAD_GLES(glCompressedTexSubImage2D);
|
|
errorGL();
|
|
if (isDXTc(format)) {
|
|
GLvoid *pixels;
|
|
if (width<4 || height<4) { // can happens :(
|
|
GLvoid *tmp;
|
|
GLsizei nw=width;
|
|
GLsizei nh=height;
|
|
if (nw<4) nw = 4;
|
|
if (nh<4) nh = 4;
|
|
tmp = uncompressDXTc(nw, nh, format, imageSize, datab);
|
|
pixels = malloc(4*width*height);
|
|
// crop
|
|
for (int y=0; y<height; y++)
|
|
memcpy(pixels+y*width*4, tmp+y*nw*4, width*4);
|
|
free(tmp);
|
|
} else {
|
|
pixels = uncompressDXTc(width, height, format, imageSize, datab);
|
|
}
|
|
GLvoid *half=pixels;
|
|
#if 1
|
|
pixel_thirdscale(pixels, &half, width, height, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
int oldalign;
|
|
glGetIntegerv(GL_UNPACK_ALIGNMENT, &oldalign);
|
|
if (oldalign!=1) glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
glTexSubImage2D(target, level, xoffset/2, yoffset/2, width/2, height/2, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, half);
|
|
if (oldalign!=1) glPixelStorei(GL_UNPACK_ALIGNMENT, oldalign);
|
|
#else
|
|
pixel_halfscale(pixels, &half, width, height, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
glTexSubImage2D(target, level, xoffset/2, yoffset/2, width/2, height/2, GL_RGBA, GL_UNSIGNED_BYTE, half);
|
|
#endif
|
|
if (half!=pixels)
|
|
free(half);
|
|
if (pixels!=datab)
|
|
free(pixels);
|
|
} else {
|
|
gles_glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, datab);
|
|
}
|
|
|
|
}
|
|
|
|
void glGetCompressedTexImage(GLenum target, GLint lod, GLvoid *img) {
|
|
if (state.gl_batch) flush();
|
|
|
|
printf("LIBGL: Stub GetCompressedTexImage\n");
|
|
|
|
errorShim(GL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
void glCompressedTexImage1D(GLenum target, GLint level, GLenum internalformat,
|
|
GLsizei width, GLint border,
|
|
GLsizei imageSize, const GLvoid *data) {
|
|
|
|
glCompressedTexImage2D(target, level, internalformat, width, 1, border, imageSize, data);
|
|
}
|
|
|
|
void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalformat,
|
|
GLsizei width, GLsizei height, GLsizei depth, GLint border,
|
|
GLsizei imageSize, const GLvoid *data) {
|
|
|
|
glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data);
|
|
}
|
|
|
|
void glCompressedTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
|
GLsizei width, GLenum format,
|
|
GLsizei imageSize, const GLvoid *data) {
|
|
|
|
glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, 1, format, imageSize, data);
|
|
}
|
|
void glCompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
|
|
GLsizei width, GLsizei height, GLsizei depth, GLenum format,
|
|
GLsizei imageSize, const GLvoid *data) {
|
|
|
|
glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
|
|
}
|