/*(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 #include #include #if HAS_SDL_OPENGL_H #include #else #ifdef WIN32 #include #endif #if defined(__APPLE__) && defined(__MACH__) #include #include #else #include #include #endif #endif #if defined(_WIN32) && !defined(APIENTRY) && \ !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #include #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 = ""; 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 = ""; 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 */