This is the first commit of the SDL 1.2 -> SDL 3 migration. It lands
the build-system change and the include-path rewrites; the code does
NOT compile against SDL 3 yet, by design. Stays on this branch until
the migration is finished.
- CMakeLists.txt: find_package(SDL3 CONFIG) + SDL3::SDL3 /
SDL3_image::SDL3_image. Drops the legacy FindSDL machinery.
- glSDL.h, sprite.c, glSDL.c, filters.c, gfxengine.cpp, audio.c:
rewrite #include "SDL*.h" -> <SDL3/...> / <SDL3_image/...>.
Known wall: graphics/glSDL.{c,h} collides with SDL 3's surface API
renames (SDL_FreeSurface, SDL_FillRect, SDL_SetColorKey,
SDL_SetClipRect, SDL_bool). The next phase deletes the entire glSDL
shim and rewrites graphics/ against SDL_Renderer / SDL_Texture.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2329 lines
48 KiB
C
2329 lines
48 KiB
C
/*(LGPL)
|
|
------------------------------------------------------------
|
|
glSDL 0.9 - SDL 2D API on top of OpenGL
|
|
------------------------------------------------------------
|
|
* Copyright (C) 2001-2004, 2006-2007 David Olofson
|
|
* This code is released under the terms of the GNU LGPL.
|
|
*/
|
|
|
|
#define _GLSDL_NO_REDEFINES_
|
|
#include "glSDL.h"
|
|
|
|
#ifdef HAVE_OPENGL
|
|
|
|
#define LEAK_TRACKING
|
|
|
|
#define DBG(x) /*error messages, warnings*/
|
|
#define DBG2(x) /*texture allocation*/
|
|
#define DBG3(x) /*chopping/tiling*/
|
|
#define DBG4(x) /*texture uploading*/
|
|
#define DBG5(x) /*two-way chopping/tiling*/
|
|
#define DBG6(x) /*OpenGL lib loading*/
|
|
|
|
#undef CKSTATS /*colorkey statistics*/
|
|
|
|
/* Keep this on for now! Makes large surfaces faster. */
|
|
#define FAKE_MAXTEXSIZE 256
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
#if HAS_SDL_OPENGL_H
|
|
#include <SDL3/SDL_opengl.h>
|
|
#else
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#include <OpenGL/gl.h>
|
|
#include <OpenGL/glu.h>
|
|
#else
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_WIN32) && !defined(APIENTRY) && \
|
|
!defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
|
#include <windows.h>
|
|
#endif
|
|
#ifndef APIENTRY
|
|
#define APIENTRY
|
|
#endif
|
|
|
|
|
|
static inline void clip_rect(SDL_Rect *r, SDL_Rect *to)
|
|
{
|
|
int dx1 = r->x;
|
|
int dy1 = r->y;
|
|
int dx2 = dx1 + r->w;
|
|
int dy2 = dy1 + r->h;
|
|
if(dx1 < to->x)
|
|
dx1 = to->x;
|
|
if(dy1 < to->y)
|
|
dy1 = to->y;
|
|
if(dx2 > to->x + to->w)
|
|
dx2 = to->x + to->w;
|
|
if(dy2 > to->y + to->h)
|
|
dy2 = to->y + to->h;
|
|
if(dx2 < dx1 || dy2 < dy1)
|
|
{
|
|
r->x = r->y = 0;
|
|
r->w = r->h = 0;
|
|
}
|
|
else
|
|
{
|
|
r->x = dx1;
|
|
r->y = dy1;
|
|
r->w = dx2 - dx1;
|
|
r->h = dy2 - dy1;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
OpenGL interface
|
|
----------------------------------------------------------*/
|
|
|
|
static struct
|
|
{
|
|
void (APIENTRY *Begin)(GLenum);
|
|
void (APIENTRY *BindTexture)(GLenum, GLuint);
|
|
void (APIENTRY *BlendFunc)(GLenum, GLenum);
|
|
void (APIENTRY *Color4ub)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);
|
|
void (APIENTRY *DeleteTextures)(GLsizei n, const GLuint *textures);
|
|
void (APIENTRY *Disable)(GLenum cap);
|
|
void (APIENTRY *Enable)(GLenum cap);
|
|
void (APIENTRY *End)(void);
|
|
void (APIENTRY *Flush)(void);
|
|
void (APIENTRY *GenTextures)(GLsizei n, GLuint *textures);
|
|
GLenum (APIENTRY *GetError)(void);
|
|
void (APIENTRY *GetIntegerv)(GLenum pname, GLint *params);
|
|
void (APIENTRY *LoadIdentity)(void);
|
|
void (APIENTRY *MatrixMode)(GLenum mode);
|
|
void (APIENTRY *Ortho)(GLdouble left, GLdouble right, GLdouble bottom,
|
|
GLdouble top, GLdouble zNear, GLdouble zFar);
|
|
void (APIENTRY *PixelStorei)(GLenum pname, GLint param);
|
|
void (APIENTRY *ReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height,
|
|
GLenum format, GLenum type, GLvoid *pixels);
|
|
void (APIENTRY *TexCoord2f)(GLfloat s, GLfloat t);
|
|
void (APIENTRY *TexImage2D)(GLenum target, GLint level, GLint internalformat,
|
|
GLsizei width, GLsizei height, GLint border,
|
|
GLenum format, GLenum type, const GLvoid *pixels);
|
|
void (APIENTRY *TexParameteri)(GLenum target, GLenum pname, GLint param);
|
|
void (APIENTRY *TexSubImage2D)(GLenum target, GLint level,
|
|
GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
|
|
GLenum format, GLenum type, const GLvoid *pixels);
|
|
void (APIENTRY *Translatef)(GLfloat x, GLfloat y, GLfloat z);
|
|
void (APIENTRY *Vertex2i)(GLint x, GLint y);
|
|
void (APIENTRY *Viewport)(GLint x, GLint y, GLsizei width, GLsizei height);
|
|
void (APIENTRY *Rotated)(GLdouble, GLdouble, GLdouble, GLdouble);
|
|
void (APIENTRY *Scalef)(GLfloat, GLfloat, GLfloat);
|
|
void (APIENTRY *PushMatrix)(void);
|
|
void (APIENTRY *PopMatrix)(void);
|
|
} gl;
|
|
|
|
|
|
static int GetGL(void)
|
|
{
|
|
int i;
|
|
struct
|
|
{
|
|
const char *name;
|
|
void **fn;
|
|
} glfuncs[] = {
|
|
{"glBegin", (void *)&gl.Begin },
|
|
{"glBindTexture", (void *)&gl.BindTexture },
|
|
{"glBlendFunc", (void *)&gl.BlendFunc },
|
|
{"glColor4ub", (void *)&gl.Color4ub },
|
|
{"glDeleteTextures", (void *)&gl.DeleteTextures },
|
|
{"glDisable", (void *)&gl.Disable },
|
|
{"glEnable", (void *)&gl.Enable },
|
|
{"glEnd", (void *)&gl.End },
|
|
{"glFlush", (void *)&gl.Flush },
|
|
{"glGenTextures", (void *)&gl.GenTextures },
|
|
{"glGetError", (void *)&gl.GetError },
|
|
{"glGetIntegerv", (void *)&gl.GetIntegerv },
|
|
{"glLoadIdentity", (void *)&gl.LoadIdentity },
|
|
{"glMatrixMode", (void *)&gl.MatrixMode },
|
|
{"glOrtho", (void *)&gl.Ortho },
|
|
{"glPixelStorei", (void *)&gl.PixelStorei },
|
|
{"glReadPixels", (void *)&gl.ReadPixels },
|
|
{"glTexCoord2f", (void *)&gl.TexCoord2f },
|
|
{"glTexImage2D", (void *)&gl.TexImage2D },
|
|
{"glTexParameteri", (void *)&gl.TexParameteri },
|
|
{"glTexSubImage2D", (void *)&gl.TexSubImage2D },
|
|
{"glTranslatef", (void *)&gl.Translatef },
|
|
{"glVertex2i", (void *)&gl.Vertex2i },
|
|
{"glViewport", (void *)&gl.Viewport },
|
|
{"glRotated", (void *)&gl.Rotated },
|
|
{"glScalef", (void *)&gl.Scalef },
|
|
{"glPushMatrix", (void *)&gl.PushMatrix },
|
|
{"glPopMatrix", (void *)&gl.PopMatrix },
|
|
{NULL, NULL }
|
|
};
|
|
for(i = 0; glfuncs[i].name; ++i)
|
|
{
|
|
*glfuncs[i].fn = SDL_GL_GetProcAddress(glfuncs[i].name);
|
|
if(!*glfuncs[i].fn)
|
|
{
|
|
DBG6(fprintf(stderr,"glSDL/wrapper ERROR: Could not get "
|
|
"OpenGL function '%s'!\n",
|
|
glfuncs[i].name);)
|
|
return -1;
|
|
}
|
|
DBG6(printf("Got OpenGL function '%s'.\n", glfuncs[i].name);)
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int LoadGL(void)
|
|
{
|
|
if(GetGL() < 0)
|
|
{
|
|
DBG6(printf("Couldn't get GL funcs! Trying to load lib...\n");)
|
|
SDL_GL_LoadLibrary(NULL);
|
|
if(GetGL() < 0)
|
|
{
|
|
DBG6(printf("Still couldn't get GL funcs!\n");)
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void UnloadGL(void)
|
|
{
|
|
}
|
|
|
|
|
|
static void print_glerror(int point)
|
|
{
|
|
#if (DBG(1)+0 == 1)
|
|
const char *err = "<unknown>";
|
|
switch(gl.GetError())
|
|
{
|
|
case GL_NO_ERROR:
|
|
return;
|
|
case GL_INVALID_ENUM:
|
|
err = "GL_INVALID_ENUM";
|
|
break;
|
|
case GL_INVALID_VALUE:
|
|
err = "GL_INVALID_VALUE";
|
|
break;
|
|
case GL_INVALID_OPERATION:
|
|
err = "GL_INVALID_OPERATION";
|
|
break;
|
|
case GL_STACK_OVERFLOW:
|
|
err = "GL_STACK_OVERFLOW";
|
|
break;
|
|
case GL_STACK_UNDERFLOW:
|
|
err = "GL_STACK_UNDERFLOW";
|
|
break;
|
|
case GL_OUT_OF_MEMORY:
|
|
err = "GL_OUT_OF_MEMORY";
|
|
break;
|
|
default:
|
|
err = "<unknown>";
|
|
break;
|
|
}
|
|
fprintf(stderr,"OpenGL error \"%s\" at point %d.\n", err, point);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
OpenGL state wrapper
|
|
----------------------------------------------------------*/
|
|
|
|
static struct
|
|
{
|
|
int do_blend;
|
|
int do_texture;
|
|
GLint texture;
|
|
GLenum sfactor, dfactor;
|
|
} glstate;
|
|
|
|
static void gl_reset(void)
|
|
{
|
|
glstate.do_blend = -1;
|
|
glstate.do_blend = -1;
|
|
glstate.texture = -1;
|
|
glstate.sfactor = 0xffffffff;
|
|
glstate.dfactor = 0xffffffff;
|
|
}
|
|
|
|
static __inline__ void gl_do_blend(int on)
|
|
{
|
|
if(glstate.do_blend == on)
|
|
return;
|
|
|
|
if(on)
|
|
gl.Enable(GL_BLEND);
|
|
else
|
|
gl.Disable(GL_BLEND);
|
|
glstate.do_blend = on;
|
|
}
|
|
|
|
static __inline__ void gl_do_texture(int on)
|
|
{
|
|
if(glstate.do_texture == on)
|
|
return;
|
|
|
|
if(on)
|
|
gl.Enable(GL_TEXTURE_2D);
|
|
else
|
|
gl.Disable(GL_TEXTURE_2D);
|
|
glstate.do_texture = on;
|
|
}
|
|
|
|
static __inline__ void gl_blendfunc(GLenum sfactor, GLenum dfactor)
|
|
{
|
|
if((sfactor == glstate.sfactor) && (dfactor == glstate.dfactor))
|
|
return;
|
|
|
|
gl.BlendFunc(sfactor, dfactor);
|
|
|
|
glstate.sfactor = sfactor;
|
|
glstate.dfactor = dfactor;
|
|
}
|
|
|
|
static __inline__ void gl_texture(GLuint tx)
|
|
{
|
|
if(tx == (unsigned)glstate.texture)
|
|
return;
|
|
|
|
gl.BindTexture(GL_TEXTURE_2D, tx);
|
|
glstate.texture = tx;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Global stuff
|
|
----------------------------------------------------------*/
|
|
|
|
static int initialized = 0;
|
|
|
|
static int using_glsdl = 0;
|
|
#define USING_GLSDL (0 != using_glsdl)
|
|
|
|
#define MAX_TEXINFOS 16384
|
|
|
|
static glSDL_TexInfo **texinfotab = NULL;
|
|
static GLint maxtexsize = 256;
|
|
static SDL_PixelFormat RGBfmt, RGBAfmt;
|
|
|
|
static void UnloadTexture(glSDL_TexInfo *txi);
|
|
|
|
static int scale = 1;
|
|
static int vsync = 1;
|
|
|
|
static SDL_Surface *fake_screen = NULL;
|
|
|
|
|
|
static int glSDL_BlitGL(SDL_Surface *src, SDL_Rect *srcrect,
|
|
SDL_Surface *dst, SDL_Rect *dstrect);
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
glSDL Global State
|
|
----------------------------------------------------------*/
|
|
|
|
static struct
|
|
{
|
|
Uint8 alpha; /* Alpha */
|
|
Uint8 r, g, b; /* Color modulation */
|
|
float cx, cy; /* Transform center offset */
|
|
float sx, sy; /* Scale */
|
|
float rot; /* Rotation */
|
|
} state;
|
|
|
|
|
|
void glSDL_SetBlendAlpha(Uint8 alpha)
|
|
{
|
|
state.alpha = alpha;
|
|
}
|
|
|
|
|
|
void glSDL_SetBlendColor(Uint8 r, Uint8 g, Uint8 b)
|
|
{
|
|
state.r = r;
|
|
state.g = g;
|
|
state.b = b;
|
|
}
|
|
|
|
|
|
void glSDL_SetCenter(float x, float y)
|
|
{
|
|
state.cx = x;
|
|
state.cy = y;
|
|
}
|
|
|
|
|
|
void glSDL_SetRotation(float angle)
|
|
{
|
|
state.rot = angle;
|
|
}
|
|
|
|
|
|
void glSDL_SetScale(float x, float y)
|
|
{
|
|
state.sx = x;
|
|
state.sy = y;
|
|
}
|
|
|
|
|
|
void glSDL_ResetState(void)
|
|
{
|
|
glSDL_SetBlendAlpha(255);
|
|
glSDL_SetBlendColor(255, 255, 255);
|
|
glSDL_SetCenter(0.0f, 0.0f);
|
|
glSDL_SetRotation(0.0f);
|
|
glSDL_SetScale(1.0f, 1.0f);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Code
|
|
----------------------------------------------------------*/
|
|
|
|
/* Get texinfo for a surface. */
|
|
static __inline__ glSDL_TexInfo *glSDL_GetTexInfo(SDL_Surface *surface)
|
|
{
|
|
if(texinfotab)
|
|
return texinfotab[surface->unused1];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Allocate a "blank" texinfo for a suface. */
|
|
glSDL_TexInfo *glSDL_AllocTexInfo(SDL_Surface *surface)
|
|
{
|
|
int handle, i = 0;
|
|
glSDL_TexInfo *txi;
|
|
if(!surface)
|
|
return NULL;
|
|
|
|
txi = glSDL_GetTexInfo(surface);
|
|
if(txi)
|
|
return txi; /* There already is one! --> */
|
|
|
|
/* Find a free handle... */
|
|
handle = -1;
|
|
for(i = 1; i < MAX_TEXINFOS + 1; ++i)
|
|
if(NULL == texinfotab[i])
|
|
{
|
|
handle = i;
|
|
break;
|
|
}
|
|
|
|
if(handle < 0)
|
|
{
|
|
DBG(fprintf(stderr, "glSDL/wrapper: Out of handles!\n"));
|
|
return NULL;
|
|
}
|
|
|
|
/* ...and hook a new texinfo struct up to it. */
|
|
texinfotab[handle] = (glSDL_TexInfo*)calloc(1, sizeof(glSDL_TexInfo));
|
|
if(!texinfotab[handle])
|
|
return NULL;
|
|
|
|
/* Connect the surface to the new TexInfo. */
|
|
surface->unused1 = (Uint32)handle;
|
|
|
|
DBG2(fprintf(stderr, "glSDL/wrapper: Allocated TexInfo %d.\n", handle));
|
|
|
|
return texinfotab[handle];
|
|
}
|
|
|
|
|
|
static void FreeTexInfo(Uint32 handle)
|
|
{
|
|
if(handle >= MAX_TEXINFOS)
|
|
return;
|
|
if(!texinfotab[handle])
|
|
return;
|
|
|
|
UnloadTexture(texinfotab[handle]);
|
|
texinfotab[handle]->textures = 0;
|
|
free(texinfotab[handle]->texture);
|
|
texinfotab[handle]->texture = NULL;
|
|
free(texinfotab[handle]);
|
|
texinfotab[handle] = NULL;
|
|
DBG2(fprintf(stderr, "glSDL/wrapper: Freed TexInfo %d.\n", handle));
|
|
}
|
|
|
|
|
|
/* Detach and free the texinfo of a surface. */
|
|
void glSDL_FreeTexInfo(SDL_Surface *surface)
|
|
{
|
|
if(!texinfotab)
|
|
return;
|
|
|
|
if(!surface)
|
|
return;
|
|
|
|
if(!glSDL_GetTexInfo(surface))
|
|
return;
|
|
|
|
FreeTexInfo(surface->unused1);
|
|
GLSDL_FIX_SURFACE(surface);
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate chopping/tiling of a surface to
|
|
* fit it into the smallest possible OpenGL
|
|
* texture.
|
|
*/
|
|
static int CalcChop(SDL_Surface *s, glSDL_TexInfo *txi)
|
|
{
|
|
int rows, vw, vh;
|
|
int vertical = 0;
|
|
int texsize;
|
|
int lastw, lasth, minsize;
|
|
|
|
vw = s->w;
|
|
vh = s->h;
|
|
|
|
DBG3(fprintf(stderr, "w=%d, h=%d ", vw, vh));
|
|
if(vh > vw)
|
|
{
|
|
int t = vw;
|
|
vw = vh;
|
|
vh = t;
|
|
vertical = 1;
|
|
DBG3(fprintf(stderr, "(vertical) \t"));
|
|
}
|
|
|
|
/*
|
|
* Check whether this is a "huge" surface - at least one dimension
|
|
* must be <= than the maximum texture size, or we'll have to chop
|
|
* in both directions.
|
|
*/
|
|
if(vh > maxtexsize)
|
|
{
|
|
/*
|
|
* Very simple hack for now; we just tile
|
|
* both ways with maximum size textures.
|
|
*/
|
|
texsize = maxtexsize;
|
|
|
|
txi->tilemode = GLSDL_TM_HUGE;
|
|
txi->texsize = texsize;
|
|
txi->tilew = texsize;
|
|
txi->tileh = texsize;
|
|
txi->tilespertex = 1;
|
|
|
|
/* Calculate number of textures needed */
|
|
txi->textures = (vw + texsize - 1) / texsize;
|
|
txi->textures *= (vh + texsize - 1) / texsize;
|
|
txi->texture = (int*)malloc(txi->textures * sizeof(int));
|
|
memset(txi->texture, -1, txi->textures * sizeof(int));
|
|
DBG5(fprintf(stderr, "two-way tiling; textures=%d\n", txi->textures));
|
|
if(!txi->texture)
|
|
{
|
|
fprintf(stderr, "glSDL/wrapper: INTERNAL ERROR: Failed to allocate"
|
|
" texture name table!\n");
|
|
return -3;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Calculate minimum size */
|
|
rows = 1;
|
|
lastw = vw;
|
|
lasth = vh;
|
|
minsize = lastw > lasth ? lastw : lasth;
|
|
while(1)
|
|
{
|
|
int w, h, size;
|
|
++rows;
|
|
w = vw / rows;
|
|
h = rows * vh;
|
|
size = w > h ? w : h;
|
|
if(size >= minsize)
|
|
{
|
|
--rows;
|
|
break;
|
|
}
|
|
lastw = w;
|
|
lasth = h;
|
|
minsize = size;
|
|
}
|
|
if(minsize > maxtexsize)
|
|
{
|
|
/* Handle multiple textures for very wide/tall surfaces. */
|
|
minsize = maxtexsize;
|
|
rows = (vw + minsize-1) / minsize;
|
|
}
|
|
DBG3(fprintf(stderr, "==> minsize=%d ", minsize));
|
|
DBG3(fprintf(stderr, "(rows=%d) \t", rows));
|
|
|
|
/* Recalculate with nearest higher power-of-2 width. */
|
|
for(texsize = 1; texsize < minsize; texsize <<= 1)
|
|
;
|
|
txi->texsize = texsize;
|
|
rows = (vw + texsize-1) / texsize;
|
|
DBG3(fprintf(stderr, "==> texsize=%d (rows=%d) \t", texsize, rows));
|
|
|
|
/* Calculate number of tiles per texture */
|
|
txi->tilespertex = txi->texsize / vh;
|
|
DBG3(fprintf(stderr, "tilespertex=%d \t", txi->tilespertex));
|
|
|
|
/* Calculate number of textures needed */
|
|
txi->textures = (rows + txi->tilespertex-1) / txi->tilespertex;
|
|
txi->texture = (int*)malloc(txi->textures * sizeof(int));
|
|
memset(txi->texture, -1, txi->textures * sizeof(int));
|
|
DBG3(fprintf(stderr, "textures=%d, ", txi->textures));
|
|
if(!txi->texture)
|
|
{
|
|
fprintf(stderr, "glSDL/wrapper: INTERNAL ERROR: Failed to allocate"
|
|
" texture name table!\n");
|
|
return -2;
|
|
}
|
|
|
|
/* Set up tile size. (Only one axis supported here!) */
|
|
if(1 == rows)
|
|
{
|
|
txi->tilemode = GLSDL_TM_SINGLE;
|
|
if(vertical)
|
|
{
|
|
txi->tilew = vh;
|
|
txi->tileh = vw;
|
|
}
|
|
else
|
|
{
|
|
txi->tilew = vw;
|
|
txi->tileh = vh;
|
|
}
|
|
}
|
|
else if(vertical)
|
|
{
|
|
txi->tilemode = GLSDL_TM_VERTICAL;
|
|
txi->tilew = vh;
|
|
txi->tileh = texsize;
|
|
}
|
|
else
|
|
{
|
|
txi->tilemode = GLSDL_TM_HORIZONTAL;
|
|
txi->tilew = texsize;
|
|
txi->tileh = vh;
|
|
}
|
|
|
|
DBG3(fprintf(stderr, "tilew=%d, tileh=%d\n", txi->tilew, txi->tileh));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Add a glSDL_TexInfo struct to an SDL_Surface */
|
|
static int glSDL_AddTexInfo(SDL_Surface *surface)
|
|
{
|
|
glSDL_TexInfo *txi;
|
|
|
|
if(!surface)
|
|
return -1;
|
|
if(IS_GLSDL_SURFACE(surface))
|
|
return 0; /* Do nothing */
|
|
|
|
glSDL_AllocTexInfo(surface);
|
|
txi = glSDL_GetTexInfo(surface);
|
|
if(!txi)
|
|
return -2; /* Oops! Didn't get a texinfo... --> */
|
|
|
|
if(CalcChop(surface, txi) < 0)
|
|
return -3;
|
|
|
|
SDL_SetClipRect(surface, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Create a surface of the prefered OpenGL RGB texture format */
|
|
static SDL_Surface *CreateRGBSurface(int w, int h)
|
|
{
|
|
SDL_Surface *s;
|
|
Uint32 rmask, gmask, bmask;
|
|
int bits = 24;
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
rmask = 0x00ff0000;
|
|
gmask = 0x0000ff00;
|
|
bmask = 0x000000ff;
|
|
#else
|
|
rmask = 0x000000ff;
|
|
gmask = 0x0000ff00;
|
|
bmask = 0x00ff0000;
|
|
#endif
|
|
s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
|
|
bits, rmask, gmask, bmask, 0);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
|
|
glSDL_AddTexInfo(s);
|
|
return s;
|
|
}
|
|
|
|
|
|
/* Create a surface of the prefered OpenGL RGBA texture format */
|
|
static SDL_Surface *CreateRGBASurface(int w, int h)
|
|
{
|
|
SDL_Surface *s;
|
|
Uint32 rmask, gmask, bmask, amask;
|
|
int bits = 32;
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
rmask = 0xff000000;
|
|
gmask = 0x00ff0000;
|
|
bmask = 0x0000ff00;
|
|
amask = 0x000000ff;
|
|
#else
|
|
rmask = 0x000000ff;
|
|
gmask = 0x0000ff00;
|
|
bmask = 0x00ff0000;
|
|
amask = 0xff000000;
|
|
#endif
|
|
s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
|
|
bits, rmask, gmask, bmask, amask);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
|
|
glSDL_AddTexInfo(s);
|
|
return s;
|
|
}
|
|
|
|
|
|
static void init_formats(void)
|
|
{
|
|
SDL_Surface *s = CreateRGBSurface(1, 1);
|
|
if(!s)
|
|
return;
|
|
RGBfmt = *(s->format);
|
|
glSDL_FreeSurface(s);
|
|
|
|
s = CreateRGBASurface(1, 1);
|
|
if(!s)
|
|
return;
|
|
RGBAfmt = *(s->format);
|
|
glSDL_FreeSurface(s);
|
|
}
|
|
|
|
|
|
static int FormatIsOk(SDL_Surface *surface)
|
|
{
|
|
SDL_PixelFormat *pf;
|
|
if(!surface)
|
|
return 1; /* Well, there ain't much we can do anyway... */
|
|
|
|
pf = surface->format;
|
|
|
|
/* Colorkeying always requires conversion! */
|
|
if(surface->flags & SDL_SRCCOLORKEY)
|
|
return 0;
|
|
|
|
/* We need pitch == (width * BytesPerPixel) for glTex[Sub]Image2D() */
|
|
if(surface->pitch != (surface->w * pf->BytesPerPixel))
|
|
return 0;
|
|
|
|
if(pf->Amask)
|
|
{
|
|
if(pf->BytesPerPixel != RGBAfmt.BytesPerPixel)
|
|
return 0;
|
|
if(pf->Rmask != RGBAfmt.Rmask)
|
|
return 0;
|
|
if(pf->Gmask != RGBAfmt.Gmask)
|
|
return 0;
|
|
if(pf->Bmask != RGBAfmt.Bmask)
|
|
return 0;
|
|
if(pf->Amask != RGBAfmt.Amask)
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if(pf->BytesPerPixel != RGBfmt.BytesPerPixel)
|
|
return 0;
|
|
if(pf->Rmask != RGBfmt.Rmask)
|
|
return 0;
|
|
if(pf->Gmask != RGBfmt.Gmask)
|
|
return 0;
|
|
if(pf->Bmask != RGBfmt.Bmask)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
static void key2alpha(SDL_Surface *surface)
|
|
{
|
|
int x, y;
|
|
#ifdef CKSTATS
|
|
int transp = 0;
|
|
#endif
|
|
Uint32 rgbmask = surface->format->Rmask |
|
|
surface->format->Gmask |
|
|
surface->format->Bmask;
|
|
Uint32 ckey = surface->format->colorkey & rgbmask;
|
|
if(SDL_LockSurface(surface) < 0)
|
|
return;
|
|
|
|
for(y = 0; y < surface->h; ++y)
|
|
{
|
|
Uint32 *px = (Uint32 *)((char *)surface->pixels + y*surface->pitch);
|
|
for(x = 0; x < surface->w; ++x)
|
|
if((px[x] & rgbmask) == ckey)
|
|
{
|
|
px[x] = 0;
|
|
#ifdef CKSTATS
|
|
++transp;
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef CKSTATS
|
|
printf("glSDL/wrapper: key2alpha(); %dx%d surface, %d opaque pixels.\n",
|
|
surface->w, surface->h,
|
|
surface->w * surface->h - transp);
|
|
#endif
|
|
SDL_UnlockSurface(surface);
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
SDL style API
|
|
----------------------------------------------------------*/
|
|
|
|
static void KillAllTextures(void)
|
|
{
|
|
if(texinfotab)
|
|
{
|
|
unsigned i;
|
|
#ifdef LEAK_TRACKING
|
|
int leaked = 0;
|
|
for(i = 3; i < MAX_TEXINFOS + 1; ++i)
|
|
if(texinfotab[i])
|
|
{
|
|
++leaked;
|
|
fprintf(stderr, "glSDL/wrapper: Leaked TexInfo"
|
|
" %d! (%d %dx%d textures)\n",
|
|
i,
|
|
texinfotab[i]->textures,
|
|
texinfotab[i]->texsize,
|
|
texinfotab[i]->texsize
|
|
);
|
|
}
|
|
if(leaked)
|
|
fprintf(stderr, "glSDL/wrapper: Leaked %d TexInfos!\n", leaked);
|
|
#endif
|
|
for(i = 1; i < MAX_TEXINFOS + 1; ++i)
|
|
FreeTexInfo(i);
|
|
free(texinfotab);
|
|
texinfotab = NULL;
|
|
}
|
|
}
|
|
|
|
void glSDL_Quit(void)
|
|
{
|
|
if(SDL_WasInit(SDL_INIT_VIDEO))
|
|
{
|
|
glSDL_FreeTexInfo(SDL_GetVideoSurface());
|
|
if(fake_screen)
|
|
{
|
|
glSDL_FreeTexInfo(fake_screen);
|
|
SDL_FreeSurface(fake_screen);
|
|
fake_screen = NULL;
|
|
}
|
|
UnloadGL();
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
}
|
|
#ifndef LEAK_TRACKING
|
|
KillAllTextures();
|
|
#endif
|
|
}
|
|
|
|
|
|
void glSDL_FullQuit(void)
|
|
{
|
|
#ifdef LEAK_TRACKING
|
|
KillAllTextures();
|
|
#endif
|
|
glSDL_Quit();
|
|
SDL_Quit();
|
|
}
|
|
|
|
|
|
void glSDL_QuitSubSystem(Uint32 flags)
|
|
{
|
|
if(flags & SDL_INIT_VIDEO)
|
|
glSDL_Quit();
|
|
SDL_QuitSubSystem(flags);
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
|
|
{
|
|
SDL_Surface *screen;
|
|
GLint gl_doublebuf;
|
|
|
|
if(!initialized)
|
|
{
|
|
glSDL_ResetState();
|
|
initialized = 1;
|
|
}
|
|
|
|
if(USING_GLSDL)
|
|
{
|
|
glSDL_FreeTexInfo(SDL_GetVideoSurface());
|
|
if(fake_screen)
|
|
{
|
|
glSDL_FreeTexInfo(fake_screen);
|
|
SDL_FreeSurface(fake_screen);
|
|
fake_screen = NULL;
|
|
}
|
|
UnloadGL();
|
|
using_glsdl = 0;
|
|
}
|
|
|
|
if(!(flags & SDL_GLSDL))
|
|
{
|
|
screen = SDL_SetVideoMode(width, height, bpp, flags);
|
|
if(screen)
|
|
GLSDL_FIX_SURFACE(screen);
|
|
return screen;
|
|
}
|
|
|
|
if((SDL_Linked_Version()->major <= 1) &&
|
|
(SDL_Linked_Version()->minor <= 2) &&
|
|
(SDL_Linked_Version()->patch < 5))
|
|
fprintf(stderr, "glSDL/wrapper WARNING: Using SDL version"
|
|
" 1.2.5 or later is strongly"
|
|
" recommended!\n");
|
|
|
|
if(LoadGL() < 0)
|
|
{
|
|
fprintf(stderr, "glSDL/wrapper ERROR: Could not load OpenGL library!\n");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* FIXME: Here's the place to insert proper handling of this call being
|
|
* used for resizing the window... For now, just make sure we
|
|
* don't end up with invalid texinfos and stuff no matter what.
|
|
*/
|
|
KillAllTextures();
|
|
|
|
texinfotab = (glSDL_TexInfo**)calloc(MAX_TEXINFOS + 1, sizeof(glSDL_TexInfo *));
|
|
if(!texinfotab)
|
|
return NULL;
|
|
|
|
/* Remove flag to avoid confusion inside SDL - just in case! */
|
|
flags &= ~SDL_GLSDL;
|
|
|
|
flags |= SDL_OPENGL;
|
|
if(bpp == 15)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
|
}
|
|
else if(bpp == 16)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
|
}
|
|
else if(bpp >= 24)
|
|
{
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
|
}
|
|
gl_doublebuf = (flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF;
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, gl_doublebuf);
|
|
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, vsync);
|
|
scale = 1;
|
|
|
|
screen = SDL_SetVideoMode(width*scale, height*scale, bpp, flags);
|
|
if(!screen)
|
|
{
|
|
KillAllTextures();
|
|
return NULL;
|
|
}
|
|
|
|
GLSDL_FIX_SURFACE(screen);
|
|
|
|
#ifdef FAKE_MAXTEXSIZE
|
|
maxtexsize = FAKE_MAXTEXSIZE;
|
|
#else
|
|
gl.GetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize);
|
|
#endif
|
|
DBG(fprintf(stderr, "glSDL/wrapper: Max texture size: %d\n", maxtexsize));
|
|
|
|
init_formats();
|
|
gl_reset();
|
|
|
|
if(glSDL_AddTexInfo(screen) < 0)
|
|
{
|
|
DBG(fprintf(stderr, "glSDL/wrapper: Failed to add info to screen surface!\n"));
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
return NULL;
|
|
}
|
|
|
|
glSDL_SetClipRect(screen, &screen->clip_rect);
|
|
|
|
gl.Viewport(0, 0, screen->w * scale, screen->h * scale);
|
|
/*
|
|
* Note that this projection is upside down in
|
|
* relation to the OpenGL coordinate system.
|
|
*/
|
|
gl.MatrixMode(GL_PROJECTION);
|
|
gl.LoadIdentity();
|
|
gl.Ortho(0, scale * (float)screen->w, scale * (float)screen->h, 0,
|
|
-1.0, 1.0);
|
|
|
|
gl.MatrixMode(GL_MODELVIEW);
|
|
gl.LoadIdentity();
|
|
gl.Translatef(0.0f, 0.0f, 0.0f);
|
|
|
|
gl.Disable(GL_DEPTH_TEST);
|
|
gl.Disable(GL_CULL_FACE);
|
|
|
|
/*
|
|
* Create a software shadow buffer of the requested size.
|
|
* This is used for blit-from-screen and simulation of
|
|
* direct software rendering. (Dog slow crap. It's only
|
|
* legitimate use is probably screen shots.)
|
|
*/
|
|
fake_screen = CreateRGBSurface(screen->w / scale,
|
|
screen->h / scale);
|
|
using_glsdl = 1;
|
|
return fake_screen;
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_GetVideoSurface(void)
|
|
{
|
|
if(fake_screen)
|
|
return fake_screen;
|
|
else
|
|
return SDL_GetVideoSurface();
|
|
}
|
|
|
|
|
|
void glSDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects)
|
|
{
|
|
if(IS_GLSDL_SURFACE(screen))
|
|
glSDL_Flip(screen);
|
|
else
|
|
SDL_UpdateRects(screen, numrects, rects);
|
|
}
|
|
|
|
|
|
void glSDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h)
|
|
{
|
|
SDL_Rect r;
|
|
r.x = x;
|
|
r.y = y;
|
|
r.w = w;
|
|
r.h = h;
|
|
glSDL_UpdateRects(screen, 1, &r);
|
|
}
|
|
|
|
|
|
int glSDL_Flip(SDL_Surface *screen)
|
|
{
|
|
if(!IS_GLSDL_SURFACE(screen))
|
|
return SDL_Flip(screen);
|
|
|
|
SDL_GL_SwapBuffers();
|
|
return 0;
|
|
}
|
|
|
|
|
|
void glSDL_FreeSurface(SDL_Surface *surface)
|
|
{
|
|
if(!surface)
|
|
return;
|
|
glSDL_FreeTexInfo(surface);
|
|
SDL_FreeSurface(surface);
|
|
}
|
|
|
|
|
|
int glSDL_LockSurface(SDL_Surface *surface)
|
|
{
|
|
if(!surface)
|
|
return 0;
|
|
|
|
if(IS_GLSDL_SURFACE(surface))
|
|
{
|
|
if((surface == fake_screen) ||
|
|
(SDL_GetVideoSurface() == surface))
|
|
{
|
|
if(scale > 1)
|
|
return -1;
|
|
|
|
glSDL_Invalidate(fake_screen, NULL);
|
|
|
|
gl.PixelStorei(GL_UNPACK_ROW_LENGTH,
|
|
fake_screen->pitch /
|
|
fake_screen->format->BytesPerPixel);
|
|
|
|
gl.ReadPixels(0, 0, fake_screen->w, fake_screen->h,
|
|
GL_RGB, GL_UNSIGNED_BYTE,
|
|
fake_screen->pixels);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
glSDL_Invalidate(surface, NULL);
|
|
return SDL_LockSurface(surface);
|
|
}
|
|
}
|
|
else
|
|
return SDL_LockSurface(surface);
|
|
}
|
|
|
|
|
|
void glSDL_UnlockSurface(SDL_Surface *surface)
|
|
{
|
|
if(!surface)
|
|
return;
|
|
|
|
if(IS_GLSDL_SURFACE(surface))
|
|
{
|
|
glSDL_UploadSurface(surface);
|
|
if((surface == fake_screen) ||
|
|
(SDL_GetVideoSurface() == surface))
|
|
glSDL_BlitGL(fake_screen, NULL,
|
|
SDL_GetVideoSurface(), NULL);
|
|
}
|
|
else
|
|
SDL_UnlockSurface(surface);
|
|
}
|
|
|
|
|
|
int glSDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key)
|
|
{
|
|
int res = SDL_SetColorKey(surface, flag, key);
|
|
if(res < 0)
|
|
return res;
|
|
/*
|
|
* If an application does this *after* SDL_DisplayFormat,
|
|
* we're basically screwed, unless we want to do an
|
|
* in-place surface conversion hack here.
|
|
*
|
|
* What we do is just kill the glSDL texinfo... No big
|
|
* deal in most cases, as glSDL only converts once anyway,
|
|
* *unless* you keep modifying the surface.
|
|
*/
|
|
if(IS_GLSDL_SURFACE(surface))
|
|
glSDL_FreeTexInfo(surface);
|
|
return res;
|
|
}
|
|
|
|
|
|
int glSDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha)
|
|
{
|
|
/*
|
|
* This is just parameters to OpenGL, so the actual
|
|
* "work" is done in glSDL_BlitSurface().
|
|
*/
|
|
return SDL_SetAlpha(surface, flag, alpha);
|
|
}
|
|
|
|
|
|
SDL_bool glSDL_SetClipRect(SDL_Surface *surface, SDL_Rect *rect)
|
|
{
|
|
SDL_bool res;
|
|
SDL_Surface *screen;
|
|
SDL_Rect fsr;
|
|
if(!surface)
|
|
return SDL_FALSE;
|
|
|
|
screen = SDL_GetVideoSurface();
|
|
|
|
res = SDL_SetClipRect(surface, rect);
|
|
if(!res)
|
|
return SDL_FALSE;
|
|
|
|
if(!rect)
|
|
{
|
|
fsr.x = 0;
|
|
fsr.y = 0;
|
|
fsr.w = screen->w;
|
|
fsr.h = screen->h;
|
|
rect = &fsr;
|
|
}
|
|
if(surface == fake_screen)
|
|
{
|
|
SDL_Rect r;
|
|
r.x = rect->x;
|
|
r.y = rect->y;
|
|
r.w = rect->w;
|
|
r.h = rect->h;
|
|
surface = screen;
|
|
SDL_SetClipRect(surface, rect);
|
|
return SDL_TRUE;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
static int glSDL_BlitFromGL(SDL_Rect *srcrect,
|
|
SDL_Surface *dst, SDL_Rect *dstrect)
|
|
{
|
|
int i, sy0, dy0;
|
|
SDL_Rect sr, dr;
|
|
|
|
if(scale > 1)
|
|
return -1;
|
|
|
|
/* In case the destination has an OpenGL texture... */
|
|
glSDL_Invalidate(dst, dstrect);
|
|
|
|
/* Abuse the fake screen buffer a little. */
|
|
gl.PixelStorei(GL_UNPACK_ROW_LENGTH, fake_screen->pitch /
|
|
fake_screen->format->BytesPerPixel);
|
|
if(srcrect)
|
|
{
|
|
sr = *srcrect;
|
|
dr.x = dr.y = 0;
|
|
dr.w = fake_screen->w;
|
|
dr.h = fake_screen->h;
|
|
clip_rect(&sr, &dr);
|
|
gl.ReadPixels(sr.x, sr.y, sr.w, sr.h, GL_RGB, GL_UNSIGNED_BYTE,
|
|
fake_screen->pixels);
|
|
}
|
|
else
|
|
{
|
|
sr.x = sr.y = 0;
|
|
sr.w = dst->w;
|
|
srcrect = &sr;
|
|
gl.ReadPixels(0, 0, fake_screen->w, fake_screen->h,
|
|
GL_RGB, GL_UNSIGNED_BYTE, fake_screen->pixels);
|
|
}
|
|
|
|
/* Blit to the actual target! (Vert. flip... Uuurgh!) */
|
|
if(dstrect)
|
|
dr = *dstrect;
|
|
else
|
|
{
|
|
dr.x = dr.y = 0;
|
|
dstrect = &dr;
|
|
}
|
|
|
|
i = srcrect->h;
|
|
sy0 = srcrect->y;
|
|
dy0 = dstrect->y + dstrect->h - 1;
|
|
while(i--)
|
|
{
|
|
sr.y = sy0 + i;
|
|
dr.y = dy0 - i;
|
|
sr.h = 1;
|
|
if(SDL_BlitSurface(fake_screen, &sr, dst, &dr) < 0)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static __inline__ void BlitGL_single(glSDL_TexInfo *txi,
|
|
float sx1, float sy1, SDL_Rect *dst, unsigned char alpha)
|
|
{
|
|
float sx2, sy2, texscale;
|
|
if(!txi->textures)
|
|
return;
|
|
if(-1 == txi->texture[0])
|
|
return;
|
|
gl_texture(txi->texture[0]);
|
|
|
|
texscale = 1.0 / (float)txi->texsize;
|
|
sx2 = (sx1 + (float)dst->w) * texscale;
|
|
sy2 = (sy1 + (float)dst->h) * texscale;
|
|
sx1 *= texscale;
|
|
sy1 *= texscale;
|
|
|
|
gl.Begin(GL_QUADS);
|
|
gl.Color4ub(state.r, state.g, state.b, alpha);
|
|
gl.TexCoord2f(sx1, sy1);
|
|
gl.Vertex2i(dst->x, dst->y);
|
|
gl.TexCoord2f(sx2, sy1);
|
|
gl.Vertex2i(dst->x + dst->w, dst->y);
|
|
gl.TexCoord2f(sx2, sy2);
|
|
gl.Vertex2i(dst->x + dst->w, dst->y + dst->h);
|
|
gl.TexCoord2f(sx1, sy2);
|
|
gl.Vertex2i(dst->x, dst->y + dst->h);
|
|
gl.End();
|
|
}
|
|
|
|
|
|
static void BlitGL_htile(glSDL_TexInfo *txi,
|
|
float sx1, float sy1, SDL_Rect *dst, unsigned char alpha)
|
|
{
|
|
int tex;
|
|
float tile, sx2, sy2, yo;
|
|
float texscale = 1.0 / (float)txi->texsize;
|
|
float tileh = (float)txi->tileh * texscale;
|
|
sx2 = (sx1 + (float)dst->w) * texscale;
|
|
sy2 = (sy1 + (float)dst->h) * texscale;
|
|
sx1 *= texscale;
|
|
sy1 *= texscale;
|
|
tile = floor(sx1);
|
|
tex = (int)tile / txi->tilespertex;
|
|
yo = ((int)tile % txi->tilespertex) * tileh;
|
|
|
|
if(tex >= txi->textures)
|
|
return;
|
|
if(-1 == txi->texture[tex])
|
|
return;
|
|
gl_texture(txi->texture[tex]);
|
|
|
|
gl.Begin(GL_QUADS);
|
|
while(tile < sx2)
|
|
{
|
|
int tdx1 = dst->x;
|
|
int tdx2 = dst->x + dst->w;
|
|
float tsx1 = sx1 - tile;
|
|
float tsx2 = sx2 - tile;
|
|
|
|
/* Clip to current tile */
|
|
if(tsx1 < 0.0)
|
|
{
|
|
tdx1 -= (int)(tsx1 * txi->texsize);
|
|
tsx1 = 0.0;
|
|
}
|
|
if(tsx2 > 1.0)
|
|
{
|
|
tdx2 -= (int)((tsx2 - 1.0) * txi->texsize);
|
|
tsx2 = 1.0;
|
|
}
|
|
|
|
/* Maybe select next texture? */
|
|
if(yo + tileh > 1.0)
|
|
{
|
|
++tex;
|
|
gl.End();
|
|
if(tex >= txi->textures)
|
|
return;
|
|
if(-1 == txi->texture[tex])
|
|
return;
|
|
gl_texture(txi->texture[tex]);
|
|
yo = 0.0;
|
|
gl.Begin(GL_QUADS);
|
|
}
|
|
|
|
gl.Color4ub(state.r, state.g, state.b, alpha);
|
|
gl.TexCoord2f(tsx1, yo + sy1);
|
|
gl.Vertex2i(tdx1, dst->y);
|
|
gl.TexCoord2f(tsx2, yo + sy1);
|
|
gl.Vertex2i(tdx2, dst->y);
|
|
gl.TexCoord2f(tsx2, yo + sy2);
|
|
gl.Vertex2i(tdx2, dst->y + dst->h);
|
|
gl.TexCoord2f(tsx1, yo + sy2);
|
|
gl.Vertex2i(tdx1, dst->y + dst->h);
|
|
|
|
tile += 1.0;
|
|
yo += tileh;
|
|
}
|
|
gl.End();
|
|
}
|
|
|
|
|
|
static void BlitGL_vtile(glSDL_TexInfo *txi,
|
|
float sx1, float sy1, SDL_Rect *dst, unsigned char alpha)
|
|
{
|
|
int tex;
|
|
float tile, sx2, sy2, xo;
|
|
float texscale = 1.0 / (float)txi->texsize;
|
|
float tilew = (float)txi->tilew * texscale;
|
|
sx2 = (sx1 + (float)dst->w) * texscale;
|
|
sy2 = (sy1 + (float)dst->h) * texscale;
|
|
sx1 *= texscale;
|
|
sy1 *= texscale;
|
|
tile = floor(sy1);
|
|
tex = (int)tile / txi->tilespertex;
|
|
xo = ((int)tile % txi->tilespertex) * tilew;
|
|
|
|
if(tex >= txi->textures)
|
|
return;
|
|
if(-1 == txi->texture[tex])
|
|
return;
|
|
gl_texture(txi->texture[tex]);
|
|
|
|
gl.Begin(GL_QUADS);
|
|
while(tile < sy2)
|
|
{
|
|
int tdy1 = dst->y;
|
|
int tdy2 = dst->y + dst->h;
|
|
float tsy1 = sy1 - tile;
|
|
float tsy2 = sy2 - tile;
|
|
|
|
/* Clip to current tile */
|
|
if(tsy1 < 0.0)
|
|
{
|
|
tdy1 -= (int)(tsy1 * txi->texsize);
|
|
tsy1 = 0.0;
|
|
}
|
|
if(tsy2 > 1.0)
|
|
{
|
|
tdy2 -= (int)((tsy2 - 1.0) * txi->texsize);
|
|
tsy2 = 1.0;
|
|
}
|
|
|
|
/* Maybe select next texture? */
|
|
if(xo + tilew > 1.0)
|
|
{
|
|
++tex;
|
|
gl.End();
|
|
if(tex >= txi->textures)
|
|
return;
|
|
if(-1 == txi->texture[tex])
|
|
return;
|
|
gl_texture(txi->texture[tex]);
|
|
xo = 0.0;
|
|
gl.Begin(GL_QUADS);
|
|
}
|
|
|
|
gl.Color4ub(state.r, state.g, state.b, alpha);
|
|
gl.TexCoord2f(xo + sx1, tsy1);
|
|
gl.Vertex2i(dst->x, tdy1);
|
|
gl.TexCoord2f(xo + sx2, tsy1);
|
|
gl.Vertex2i(dst->x + dst->w, tdy1);
|
|
gl.TexCoord2f(xo + sx2, tsy2);
|
|
gl.Vertex2i(dst->x + dst->w, tdy2);
|
|
gl.TexCoord2f(xo + sx1, tsy2);
|
|
gl.Vertex2i(dst->x, tdy2);
|
|
|
|
tile += 1.0;
|
|
xo += tilew;
|
|
}
|
|
gl.End();
|
|
}
|
|
|
|
|
|
static void BlitGL_hvtile(SDL_Surface *src, glSDL_TexInfo *txi,
|
|
float sx1, float sy1, SDL_Rect *dst, unsigned char alpha)
|
|
{
|
|
int x, y, last_tex, tex;
|
|
float sx2, sy2;
|
|
float texscale = 1.0 / (float)txi->texsize;
|
|
int tilesperrow = (src->w + txi->tilew - 1) / txi->tilew;
|
|
sx2 = (sx1 + (float)dst->w) * texscale;
|
|
sy2 = (sy1 + (float)dst->h) * texscale;
|
|
sx1 *= texscale;
|
|
sy1 *= texscale;
|
|
|
|
last_tex = tex = (int)(floor(sy1) * tilesperrow + floor(sx1));
|
|
if(tex >= txi->textures)
|
|
return;
|
|
if(-1 == txi->texture[tex])
|
|
return;
|
|
gl_texture(txi->texture[tex]);
|
|
|
|
gl.Begin(GL_QUADS);
|
|
for(y = (int)floor(sy1); y < sy2; ++y)
|
|
{
|
|
int tdy1 = dst->y;
|
|
int tdy2 = dst->y + dst->h;
|
|
float tsy1 = sy1 - y;
|
|
float tsy2 = sy2 - y;
|
|
|
|
/* Clip to current tile */
|
|
if(tsy1 < 0.0)
|
|
{
|
|
tdy1 -= (int)(tsy1 * txi->texsize);
|
|
tsy1 = 0.0;
|
|
}
|
|
if(tsy2 > 1.0)
|
|
{
|
|
tdy2 -= (int)((tsy2 - 1.0) * txi->texsize);
|
|
tsy2 = 1.0;
|
|
}
|
|
for(x = (int)floor(sx1); x < sx2; ++x)
|
|
{
|
|
int tdx1 = dst->x;
|
|
int tdx2 = dst->x + dst->w;
|
|
float tsx1 = sx1 - x;
|
|
float tsx2 = sx2 - x;
|
|
|
|
/* Clip to current tile */
|
|
if(tsx1 < 0.0)
|
|
{
|
|
tdx1 -= (int)(tsx1 * txi->texsize);
|
|
tsx1 = 0.0;
|
|
}
|
|
if(tsx2 > 1.0)
|
|
{
|
|
tdx2 -= (int)((tsx2 - 1.0) * txi->texsize);
|
|
tsx2 = 1.0;
|
|
}
|
|
|
|
/* Select texture */
|
|
tex = y * tilesperrow + x;
|
|
if(tex != last_tex)
|
|
{
|
|
gl.End();
|
|
if(tex >= txi->textures)
|
|
return;
|
|
if(-1 == txi->texture[tex])
|
|
return;
|
|
gl_texture(txi->texture[tex]);
|
|
last_tex = tex;
|
|
gl.Begin(GL_QUADS);
|
|
}
|
|
|
|
gl.Color4ub(state.r, state.g, state.b, alpha);
|
|
gl.TexCoord2f(tsx1, tsy1);
|
|
gl.Vertex2i(tdx1, tdy1);
|
|
gl.TexCoord2f(tsx2, tsy1);
|
|
gl.Vertex2i(tdx2, tdy1);
|
|
gl.TexCoord2f(tsx2, tsy2);
|
|
gl.Vertex2i(tdx2, tdy2);
|
|
gl.TexCoord2f(tsx1, tsy2);
|
|
gl.Vertex2i(tdx1, tdy2);
|
|
}
|
|
}
|
|
gl.End();
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate the actual blit rectangle and source offset
|
|
* for a blit from a rectangle in a surface with specified
|
|
* size to a surface with a cliprect.
|
|
*
|
|
* In: rect source rectangle
|
|
* w, h source surface size
|
|
* (x, y) destination coordinate
|
|
* clip destination clip rectangle
|
|
*
|
|
* Out: (x, y) source top-left offset
|
|
* rect destination rectangle
|
|
*
|
|
* Returns 1 if the result is visible, otherwise 0.
|
|
*/
|
|
static __inline__ int blitclip(SDL_Rect *rect, int w, int h,
|
|
int *x, int *y, SDL_Rect *clip)
|
|
{
|
|
int sx1, sy1, sx2, sy2;
|
|
int dx1, dy1, dx2, dy2;
|
|
|
|
/* Get source and destination coordinates */
|
|
sx1 = rect->x;
|
|
sy1 = rect->y;
|
|
sx2 = sx1 + rect->w;
|
|
sy2 = sy1 + rect->h;
|
|
dx1 = *x;
|
|
dy1 = *y;
|
|
|
|
/* Keep source rect inside source surface */
|
|
if(sx1 < 0)
|
|
{
|
|
dx1 -= sx1;
|
|
sx1 = 0;
|
|
}
|
|
if(sy1 < 0)
|
|
{
|
|
dy1 -= sy1;
|
|
sy1 = 0;
|
|
}
|
|
if(sx2 > w)
|
|
sx2 = w;
|
|
if(sy2 > h)
|
|
sy2 = h;
|
|
|
|
/* Cull blits from void space */
|
|
if(sx1 >= sx2 || sy1 >= sy2)
|
|
return 0;
|
|
|
|
/* Calculate destination lower-right */
|
|
dx2 = dx1 + (sx2 - sx1);
|
|
dy2 = dy1 + (sy2 - sy1);
|
|
|
|
if(clip)
|
|
{
|
|
/* Clip to destination cliprect */
|
|
if(dx1 < clip->x)
|
|
{
|
|
sx1 += clip->x - dx1;
|
|
dx1 = clip->x;
|
|
}
|
|
if(dy1 < clip->y)
|
|
{
|
|
sy1 += clip->y - dy1;
|
|
dy1 = clip->y;
|
|
}
|
|
if(dx2 > clip->x + clip->w)
|
|
dx2 = clip->x + clip->w;
|
|
if(dy2 > clip->y + clip->h)
|
|
dy2 = clip->y + clip->h;
|
|
}
|
|
|
|
/* Cull nop/off-screen blits */
|
|
if(dx1 >= dx2 || dy1 >= dy2)
|
|
return 0;
|
|
|
|
*x = sx1;
|
|
*y = sy1;
|
|
rect->x = dx1;
|
|
rect->y = dy1;
|
|
rect->w = dx2 - dx1;
|
|
rect->h = dy2 - dy1;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int glSDL_BlitGL(SDL_Surface *src, SDL_Rect *srcrect,
|
|
SDL_Surface *dst, SDL_Rect *dstrect)
|
|
{
|
|
glSDL_TexInfo *txi;
|
|
SDL_Rect r;
|
|
int x, y;
|
|
unsigned char alpha;
|
|
int plain = (state.rot == 0.0f) &&
|
|
(state.sx == 1.0f) &&
|
|
(state.sy == 1.0f);
|
|
if(!src || !dst)
|
|
return -1;
|
|
|
|
/* Get source and destination coordinates */
|
|
if(srcrect)
|
|
r = *srcrect;
|
|
else
|
|
{
|
|
r.x = r.y = 0;
|
|
r.w = src->w;
|
|
r.h = src->h;
|
|
}
|
|
if(dstrect)
|
|
{
|
|
x = dstrect->x;
|
|
y = dstrect->y;
|
|
}
|
|
else
|
|
x = y = 0;
|
|
|
|
/* Clip! */
|
|
if(!blitclip(&r, src->w, src->h, &x, &y,
|
|
plain ? &dst->clip_rect : NULL))
|
|
{
|
|
if(dstrect)
|
|
dstrect->w = dstrect->h = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Write back the resulting cliprect */
|
|
if(dstrect)
|
|
*dstrect = r;
|
|
|
|
/* Make sure we have a source with a valid texture */
|
|
glSDL_UploadSurface(src);
|
|
txi = glSDL_GetTexInfo(src);
|
|
if(!txi)
|
|
return -1;
|
|
|
|
/* Set up blending */
|
|
if(src->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY) ||
|
|
(state.alpha != 255))
|
|
{
|
|
gl_blendfunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
gl_do_blend(1);
|
|
}
|
|
else
|
|
gl_do_blend(0);
|
|
|
|
/* Enable texturing */
|
|
gl_do_texture(1);
|
|
|
|
/*
|
|
* Note that we actually *prevent* the use of "full surface alpha"
|
|
* and alpha channel in combination - to stay SDL 2D compatible.
|
|
*/
|
|
if((src->flags & SDL_SRCALPHA) &&
|
|
(!src->format->Amask || (src->flags & SDL_SRCCOLORKEY)))
|
|
alpha = src->format->alpha;
|
|
else
|
|
alpha = 255;
|
|
|
|
/* ...however, the GL only global state alpha is always applied! */
|
|
alpha = (Uint32)alpha * state.alpha * 258 >> 16;
|
|
|
|
/* Render! */
|
|
if(!plain)
|
|
{
|
|
int rcx = (r.w >> 1) + state.cx;
|
|
int rcy = (r.h >> 1) + state.cy;
|
|
gl.PushMatrix();
|
|
gl.Translatef(r.x + rcx, r.y + rcy, 0.0f);
|
|
if(state.rot)
|
|
gl.Rotated(state.rot, 0.0f, 0.0f, 1.0f);
|
|
if(state.sx || state.sy)
|
|
gl.Scalef(state.sx, state.sy, 1.0f);
|
|
r.x = -rcx;
|
|
r.y = -rcy;
|
|
}
|
|
switch(txi->tilemode)
|
|
{
|
|
case GLSDL_TM_SINGLE:
|
|
BlitGL_single(txi, x, y, &r, alpha);
|
|
break;
|
|
case GLSDL_TM_HORIZONTAL:
|
|
BlitGL_htile(txi, x, y, &r, alpha);
|
|
break;
|
|
case GLSDL_TM_VERTICAL:
|
|
BlitGL_vtile(txi, x, y, &r, alpha);
|
|
break;
|
|
case GLSDL_TM_HUGE:
|
|
BlitGL_hvtile(src, txi, x, y, &r, alpha);
|
|
break;
|
|
}
|
|
if(!plain)
|
|
gl.PopMatrix();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int glSDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,
|
|
SDL_Surface *dst, SDL_Rect *dstrect)
|
|
{
|
|
SDL_Surface *vs;
|
|
if(!src || !dst)
|
|
return -1;
|
|
|
|
/*
|
|
* Figure out what to do:
|
|
* Not using glSDL: SDL_BlitSurface()
|
|
* screen->screen: _glSDL_BlitFromGL() + _glSDL_BlitGL()
|
|
* surface->screen: _glSDL_BlitGL()
|
|
* screen->surface: _glSDL_BlitFromGL()
|
|
* surface->surface: SDL_BlitSurface()
|
|
*/
|
|
if(!USING_GLSDL)
|
|
return SDL_BlitSurface(src, srcrect, dst, dstrect);
|
|
|
|
vs = SDL_GetVideoSurface();
|
|
if(src == fake_screen)
|
|
src = vs;
|
|
if(dst == fake_screen)
|
|
dst = vs;
|
|
if(src == vs)
|
|
{
|
|
if(dst == vs)
|
|
{
|
|
glSDL_BlitFromGL(srcrect, fake_screen, dstrect);
|
|
return glSDL_BlitGL(fake_screen, srcrect,
|
|
dst, dstrect);
|
|
}
|
|
else
|
|
{
|
|
return glSDL_BlitFromGL(srcrect, dst, dstrect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(dst == vs)
|
|
{
|
|
return glSDL_BlitGL(src, srcrect,
|
|
dst, dstrect);
|
|
}
|
|
else
|
|
{
|
|
glSDL_Invalidate(dst, dstrect);
|
|
return SDL_BlitSurface(src, srcrect, dst, dstrect);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int glSDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color)
|
|
{
|
|
SDL_Surface *vs = SDL_GetVideoSurface();
|
|
SDL_PixelFormat *pf = dst->format;
|
|
int dx1, dy1, dx2, dy2;
|
|
Uint32 r, g, b;
|
|
|
|
if(dst == fake_screen)
|
|
dst = vs;
|
|
if(vs != dst)
|
|
glSDL_Invalidate(dst, dstrect);
|
|
if((vs != dst) || !USING_GLSDL)
|
|
return SDL_FillRect(dst, dstrect, color);
|
|
|
|
if(dstrect)
|
|
{
|
|
dx1 = dstrect->x;
|
|
dy1 = dstrect->y;
|
|
dx2 = dx1 + dstrect->w;
|
|
dy2 = dy1 + dstrect->h;
|
|
if(dx1 < dst->clip_rect.x)
|
|
dx1 = dst->clip_rect.x;
|
|
if(dy1 < dst->clip_rect.y)
|
|
dy1 = dst->clip_rect.y;
|
|
if(dx2 > dst->clip_rect.x + dst->clip_rect.w)
|
|
dx2 = dst->clip_rect.x + dst->clip_rect.w;
|
|
if(dy2 > dst->clip_rect.y + dst->clip_rect.h)
|
|
dy2 = dst->clip_rect.y + dst->clip_rect.h;
|
|
dstrect->x = dx1;
|
|
dstrect->y = dy1;
|
|
dstrect->w = dx2 - dx1;
|
|
dstrect->h = dy2 - dy1;
|
|
if(!dstrect->w || !dstrect->h)
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
dx1 = dst->clip_rect.x;
|
|
dy1 = dst->clip_rect.y;
|
|
dx2 = dx1 + dst->clip_rect.w;
|
|
dy2 = dy1 + dst->clip_rect.h;
|
|
}
|
|
|
|
r = color & pf->Rmask;
|
|
r = r >> pf->Rshift;
|
|
r = r << pf->Rloss;
|
|
g = color & pf->Gmask;
|
|
g = g >> pf->Gshift;
|
|
g = g << pf->Gloss;
|
|
b = color & pf->Bmask;
|
|
b = b >> pf->Bshift;
|
|
b = b << pf->Bloss;
|
|
|
|
r = r * state.r * 258 >> 16;
|
|
g = g * state.g * 258 >> 16;
|
|
b = b * state.b * 258 >> 16;
|
|
|
|
gl_do_texture(0);
|
|
if(state.alpha != 255)
|
|
{
|
|
gl_blendfunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
gl_do_blend(1);
|
|
}
|
|
else
|
|
gl_do_blend(0);
|
|
|
|
gl.Begin(GL_QUADS);
|
|
gl.Color4ub(r, g, b, state.alpha);
|
|
gl.Vertex2i(dx1, dy1);
|
|
gl.Vertex2i(dx2, dy1);
|
|
gl.Vertex2i(dx2, dy2);
|
|
gl.Vertex2i(dx1, dy2);
|
|
gl.End();
|
|
return 0;
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_DisplayFormat(SDL_Surface *surface)
|
|
{
|
|
SDL_Surface *s, *tmp;
|
|
if(USING_GLSDL)
|
|
{
|
|
int use_rgba = (surface->flags & SDL_SRCCOLORKEY) ||
|
|
((surface->flags & SDL_SRCALPHA) &&
|
|
surface->format->Amask);
|
|
if(use_rgba)
|
|
tmp = SDL_ConvertSurface(surface, &RGBAfmt, SDL_SWSURFACE);
|
|
else
|
|
tmp = SDL_ConvertSurface(surface, &RGBfmt, SDL_SWSURFACE);
|
|
if(!tmp)
|
|
return NULL;
|
|
GLSDL_FIX_SURFACE(tmp);
|
|
SDL_SetAlpha(tmp, 0, 0);
|
|
|
|
if(surface->flags & SDL_SRCCOLORKEY)
|
|
{
|
|
/*
|
|
* We drop colorkey data here, but we have to,
|
|
* or we'll run into trouble when converting,
|
|
* in particular from indexed color formats.
|
|
*/
|
|
SDL_SetColorKey(tmp, SDL_SRCCOLORKEY,
|
|
surface->format->colorkey);
|
|
key2alpha(tmp);
|
|
}
|
|
SDL_SetColorKey(tmp, 0, 0);
|
|
|
|
if(use_rgba)
|
|
s = CreateRGBASurface(surface->w, surface->h);
|
|
else
|
|
s = CreateRGBSurface(surface->w, surface->h);
|
|
if(!s)
|
|
{
|
|
glSDL_FreeSurface(tmp);
|
|
return NULL;
|
|
}
|
|
SDL_BlitSurface(tmp, NULL, s, NULL);
|
|
glSDL_FreeSurface(tmp);
|
|
|
|
if(surface->flags & SDL_SRCALPHA)
|
|
SDL_SetAlpha(s, SDL_SRCALPHA,
|
|
surface->format->alpha);
|
|
return s;
|
|
}
|
|
else
|
|
{
|
|
s = SDL_DisplayFormat(surface);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
return s;
|
|
}
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_DisplayFormatAlpha(SDL_Surface *surface)
|
|
{
|
|
SDL_Surface *s, *tmp;
|
|
if(USING_GLSDL)
|
|
{
|
|
tmp = SDL_ConvertSurface(surface, &RGBAfmt, SDL_SWSURFACE);
|
|
if(!tmp)
|
|
return NULL;
|
|
GLSDL_FIX_SURFACE(tmp);
|
|
|
|
SDL_SetAlpha(tmp, 0, 0);
|
|
SDL_SetColorKey(tmp, 0, 0);
|
|
s = CreateRGBASurface(surface->w, surface->h);
|
|
if(!s)
|
|
{
|
|
glSDL_FreeSurface(tmp);
|
|
return NULL;
|
|
}
|
|
SDL_BlitSurface(tmp, NULL, s, NULL);
|
|
glSDL_FreeSurface(tmp);
|
|
|
|
if(surface->flags & SDL_SRCCOLORKEY)
|
|
{
|
|
SDL_SetColorKey(s, SDL_SRCCOLORKEY,
|
|
surface->format->colorkey);
|
|
key2alpha(s);
|
|
}
|
|
if(surface->flags & SDL_SRCALPHA)
|
|
SDL_SetAlpha(s, SDL_SRCALPHA,
|
|
surface->format->alpha);
|
|
return s;
|
|
}
|
|
else
|
|
{
|
|
s = SDL_DisplayFormatAlpha(surface);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
return s;
|
|
}
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_ConvertSurface
|
|
(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags)
|
|
{
|
|
SDL_Surface *s = SDL_ConvertSurface(src, fmt, flags);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
return s;
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_CreateRGBSurface
|
|
(Uint32 flags, int width, int height, int depth,
|
|
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
|
|
{
|
|
SDL_Surface *s = SDL_CreateRGBSurface(flags, width, height, depth,
|
|
Rmask, Gmask, Bmask, Amask);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
return s;
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_CreateRGBSurfaceFrom(void *pixels,
|
|
int width, int height, int depth, int pitch,
|
|
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
|
|
{
|
|
SDL_Surface *s = SDL_CreateRGBSurfaceFrom(pixels,
|
|
width, height, depth, pitch,
|
|
Rmask, Gmask, Bmask, Amask);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
return s;
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_LoadBMP(const char *file)
|
|
{
|
|
SDL_Surface *s = SDL_LoadBMP(file);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
return s;
|
|
}
|
|
|
|
|
|
int glSDL_SaveBMP(SDL_Surface *surface, const char *file)
|
|
{
|
|
SDL_Rect r;
|
|
SDL_Surface *buf;
|
|
SDL_Surface *screen = SDL_GetVideoSurface();
|
|
|
|
if(!USING_GLSDL)
|
|
return SDL_SaveBMP(surface, file);
|
|
|
|
if((surface != screen) && (surface != fake_screen))
|
|
return SDL_SaveBMP(surface, file);
|
|
|
|
buf = CreateRGBSurface(fake_screen->w, fake_screen->h);
|
|
|
|
r.x = 0;
|
|
r.y = 0;
|
|
r.w = fake_screen->w;
|
|
r.h = fake_screen->h;
|
|
if(glSDL_BlitFromGL(&r, buf, &r) < 0)
|
|
return -1;
|
|
|
|
return SDL_SaveBMP(buf, file);
|
|
|
|
glSDL_FreeSurface(buf);
|
|
}
|
|
|
|
|
|
|
|
|
|
static int InitTexture(SDL_Surface *datasurf, glSDL_TexInfo *txi, int tex)
|
|
{
|
|
gl.GenTextures(1, (unsigned int *)&txi->texture[tex]);
|
|
gl.BindTexture(GL_TEXTURE_2D, txi->texture[tex]);
|
|
gl.PixelStorei(GL_UNPACK_ROW_LENGTH, datasurf->pitch /
|
|
datasurf->format->BytesPerPixel);
|
|
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
gl.TexImage2D(GL_TEXTURE_2D, 0,
|
|
datasurf->format->Amask ? GL_RGBA8 : GL_RGB8,
|
|
txi->texsize, txi->texsize, 0,
|
|
datasurf->format->Amask ? GL_RGBA : GL_RGB,
|
|
GL_UNSIGNED_BYTE, NULL);
|
|
print_glerror(1);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Image tiled horizontally (wide surface), or not at all */
|
|
static int UploadHoriz(SDL_Surface *datasurf, glSDL_TexInfo *txi)
|
|
{
|
|
int bpp = datasurf->format->BytesPerPixel;
|
|
int res;
|
|
int tex = 0;
|
|
int fromx = 0;
|
|
int toy = txi->texsize; /* To init first texture */
|
|
while(1)
|
|
{
|
|
int thistw = datasurf->w - fromx;
|
|
if(thistw > txi->tilew)
|
|
thistw = txi->tilew;
|
|
else if(thistw <= 0)
|
|
break;
|
|
if(toy + txi->tileh > txi->texsize)
|
|
{
|
|
toy = 0;
|
|
res = InitTexture(datasurf, txi, tex);
|
|
if(res < 0)
|
|
return res;
|
|
++tex;
|
|
}
|
|
gl.TexSubImage2D(GL_TEXTURE_2D, 0, 0, toy,
|
|
thistw, txi->tileh,
|
|
datasurf->format->Amask ? GL_RGBA : GL_RGB,
|
|
GL_UNSIGNED_BYTE,
|
|
(char *)datasurf->pixels + bpp * fromx);
|
|
print_glerror(2);
|
|
fromx += txi->tilew;
|
|
toy += txi->tileh;
|
|
gl.Flush();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Image tiled vertically (tall surface) */
|
|
static int UploadVert(SDL_Surface *datasurf, glSDL_TexInfo *txi)
|
|
{
|
|
int res;
|
|
int tex = 0;
|
|
int fromy = 0;
|
|
int tox = txi->texsize; /* To init first texture */
|
|
while(1)
|
|
{
|
|
int thisth = datasurf->h - fromy;
|
|
if(thisth > txi->tileh)
|
|
thisth = txi->tileh;
|
|
else if(thisth <= 0)
|
|
break;
|
|
if(tox + txi->tilew > txi->texsize)
|
|
{
|
|
tox = 0;
|
|
res = InitTexture(datasurf, txi, tex);
|
|
if(res < 0)
|
|
return res;
|
|
++tex;
|
|
}
|
|
gl.TexSubImage2D(GL_TEXTURE_2D, 0, tox, 0,
|
|
txi->tilew, thisth,
|
|
datasurf->format->Amask ? GL_RGBA : GL_RGB,
|
|
GL_UNSIGNED_BYTE,
|
|
(char *)datasurf->pixels + datasurf->pitch * fromy);
|
|
print_glerror(3);
|
|
fromy += txi->tileh;
|
|
tox += txi->tilew;
|
|
gl.Flush();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Image tiled two-way (huge surface) */
|
|
static int UploadHuge(SDL_Surface *datasurf, glSDL_TexInfo *txi)
|
|
{
|
|
int bpp = datasurf->format->BytesPerPixel;
|
|
int res;
|
|
int tex = 0;
|
|
int y = 0;
|
|
while(y < datasurf->h)
|
|
{
|
|
int x;
|
|
int thisth = datasurf->h - y;
|
|
if(thisth > txi->tileh)
|
|
thisth = txi->tileh;
|
|
x = 0;
|
|
while(x < datasurf->w)
|
|
{
|
|
int thistw = datasurf->w - x;
|
|
if(thistw > txi->tilew)
|
|
thistw = txi->tilew;
|
|
res = InitTexture(datasurf, txi, tex++);
|
|
if(res < 0)
|
|
return res;
|
|
gl.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
|
|
thistw, thisth,
|
|
datasurf->format->Amask ? GL_RGBA : GL_RGB,
|
|
GL_UNSIGNED_BYTE,
|
|
(char *)datasurf->pixels +
|
|
datasurf->pitch * y + bpp * x);
|
|
print_glerror(4);
|
|
x += txi->tilew;
|
|
gl.Flush();
|
|
}
|
|
y += txi->tileh;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Upload all textures for a surface. */
|
|
static int UploadTextures(SDL_Surface *datasurf, glSDL_TexInfo *txi)
|
|
{
|
|
switch(txi->tilemode)
|
|
{
|
|
case GLSDL_TM_SINGLE:
|
|
case GLSDL_TM_HORIZONTAL:
|
|
UploadHoriz(datasurf, txi);
|
|
break;
|
|
case GLSDL_TM_VERTICAL:
|
|
UploadVert(datasurf, txi);
|
|
break;
|
|
case GLSDL_TM_HUGE:
|
|
UploadHuge(datasurf, txi);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
SDL_Surface *glSDL_IMG_Load(const char *file)
|
|
{
|
|
SDL_Surface *s;
|
|
s = IMG_Load(file);
|
|
if(s)
|
|
GLSDL_FIX_SURFACE(s);
|
|
return s;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
glSDL specific API extensions
|
|
----------------------------------------------------------*/
|
|
|
|
int glSDL_VSync(int use)
|
|
{
|
|
int old = vsync;
|
|
if(use >= 0)
|
|
vsync = use;
|
|
return old;
|
|
}
|
|
|
|
|
|
void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area)
|
|
{
|
|
glSDL_TexInfo *txi;
|
|
if(!surface)
|
|
return;
|
|
txi = glSDL_GetTexInfo(surface);
|
|
if(!txi)
|
|
return;
|
|
if(!area)
|
|
{
|
|
txi->invalid_area.x = 0;
|
|
txi->invalid_area.y = 0;
|
|
txi->invalid_area.w = surface->w;
|
|
txi->invalid_area.h = surface->h;
|
|
return;
|
|
}
|
|
txi->invalid_area = *area;
|
|
}
|
|
|
|
|
|
int glSDL_UploadSurface(SDL_Surface *surface)
|
|
{
|
|
SDL_Surface *datasurf = surface;
|
|
glSDL_TexInfo *txi;
|
|
int i;
|
|
/*
|
|
* For now, we just assume that *every* texture needs
|
|
* conversion before uploading.
|
|
*/
|
|
|
|
/* If there's no TexInfo, add one. */
|
|
if(!IS_GLSDL_SURFACE(surface))
|
|
glSDL_AddTexInfo(surface);
|
|
|
|
txi = glSDL_GetTexInfo(surface);
|
|
if(!txi)
|
|
return -1;
|
|
|
|
/* No partial updates implemented yet... */
|
|
if(txi->invalid_area.w)
|
|
glSDL_UnloadSurface(surface);
|
|
else
|
|
{
|
|
int missing = 0;
|
|
if(txi->textures)
|
|
{
|
|
for(i = 0; i < txi->textures; ++i)
|
|
if(-1 == txi->texture[i])
|
|
{
|
|
missing = 1;
|
|
break;
|
|
}
|
|
if(!missing)
|
|
return 0; /* They're already there! */
|
|
}
|
|
}
|
|
|
|
if(txi->texsize > maxtexsize)
|
|
{
|
|
fprintf(stderr, "glSDL/wrapper: INTERNAL ERROR: Too large texture!\n");
|
|
return -1; /* This surface wasn't tiled properly... */
|
|
}
|
|
|
|
/*
|
|
* Kludge: Convert if not of preferred RGB or RGBA format.
|
|
*
|
|
* Conversion should only be done when *really* needed.
|
|
* That is, it should rarely have to be done with OpenGL
|
|
* 1.2+.
|
|
*
|
|
* Besides, any surface that's been SDL_DisplayFormat()ed
|
|
* should already be in the best known OpenGL format -
|
|
* preferably one that makes DMA w/o conversion possible.
|
|
*/
|
|
if(FormatIsOk(surface))
|
|
datasurf = surface;
|
|
else
|
|
{
|
|
DBG(fprintf(stderr, "glSDL/wrapper: WARNING: On-the-fly conversion performed!\n"));
|
|
if(surface->format->Amask)
|
|
datasurf = glSDL_DisplayFormatAlpha(surface);
|
|
else
|
|
datasurf = glSDL_DisplayFormat(surface);
|
|
if(!datasurf)
|
|
return -2;
|
|
}
|
|
|
|
if(UploadTextures(datasurf, txi) < 0)
|
|
return -3;
|
|
|
|
if(datasurf != surface)
|
|
glSDL_FreeSurface(datasurf);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void UnloadTexture(glSDL_TexInfo *txi)
|
|
{
|
|
int i;
|
|
if(SDL_WasInit(SDL_INIT_VIDEO))
|
|
for(i = 0; i < txi->textures; ++i)
|
|
gl.DeleteTextures(1, (unsigned int *)&txi->texture[i]);
|
|
memset(&txi->invalid_area, 0, sizeof(txi->invalid_area));
|
|
}
|
|
|
|
|
|
void glSDL_UnloadSurface(SDL_Surface *surface)
|
|
{
|
|
glSDL_TexInfo *txi;
|
|
if(!IS_GLSDL_SURFACE(surface))
|
|
return;
|
|
|
|
txi = glSDL_GetTexInfo(surface);
|
|
if(txi)
|
|
UnloadTexture(txi);
|
|
}
|
|
|
|
|
|
#else /* HAVE_OPENGL */
|
|
|
|
/*----------------------------------------------------------
|
|
Dummies for glSDL extensions that require OpenGL
|
|
----------------------------------------------------------*/
|
|
|
|
void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area)
|
|
{
|
|
}
|
|
|
|
int glSDL_UploadSurface(SDL_Surface *surface)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void glSDL_UnloadSurface(SDL_Surface *surface)
|
|
{
|
|
}
|
|
|
|
int glSDL_VSync(int use)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void glSDL_SetBlendAlpha(Uint8 alpha)
|
|
{
|
|
}
|
|
|
|
void glSDL_SetBlendColor(Uint8 r, Uint8 g, Uint8 b)
|
|
{
|
|
}
|
|
|
|
void glSDL_SetCenter(float x, float y)
|
|
{
|
|
}
|
|
|
|
void glSDL_SetRotation(float angle)
|
|
{
|
|
}
|
|
|
|
void glSDL_SetScale(float x, float y)
|
|
{
|
|
}
|
|
|
|
void glSDL_ResetState(void)
|
|
{
|
|
}
|
|
|
|
#endif /* HAVE_OPENGL */
|