Mercurial > sdl-ios-xcode
diff src/video/glsdl/SDL_glsdl.c @ 1658:e49147870aac SDL-1.3
glSDL support
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Mon, 01 May 2006 06:58:33 +0000 |
parents | |
children | 782fd950bd46 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/video/glsdl/SDL_glsdl.c Mon May 01 06:58:33 2006 +0000 @@ -0,0 +1,2573 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + * glSDL "SDL-over-OpenGL" video driver implemented by + * David Olofson <david@olofson.net> and + * Stephane Marchesin <stephane.marchesin@wanadoo.fr> + */ +#include <math.h> + +#include "SDL.h" +#include "SDL_error.h" +#include "SDL_video.h" +#include "SDL_mouse.h" +#include "../SDL_sysvideo.h" +#include "../SDL_pixels_c.h" + +#include "SDL_glsdl.h" + +#undef DEBUG_GLSDL +#undef DEBUG_GLSDL_CHOP +#define FAKE_MAXTEXSIZE 256 +#undef GLSDL_GRAPHICAL_DEBUG + +/* Initialization/Query functions */ + +/* Hardware surface functions */ +static int glSDL_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors); +static int glSDL_AllocHWSurface(_THIS, SDL_Surface *surface); +static int glSDL_LockHWSurface(_THIS, SDL_Surface *surface); +static int glSDL_FlipHWSurface(_THIS, SDL_Surface *surface); +static void glSDL_UnlockHWSurface(_THIS, SDL_Surface *surface); +static void glSDL_FreeHWSurface(_THIS, SDL_Surface *surface); +static int glSDL_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color); +static int glSDL_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dst); +static int glSDL_SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key); +static int glSDL_SetHWAlpha(_THIS, SDL_Surface *surface, Uint8 alpha); +static int glSDL_VideoInit(_THIS, SDL_PixelFormat *vformat); +static SDL_Rect **glSDL_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); +static void glSDL_VideoQuit(_THIS); +static void glSDL_UpdateRects(_THIS, int numrects, SDL_Rect *rects); +static SDL_Surface* glSDL_SetVideoMode(_THIS, SDL_Surface *current,int width, int height, int bpp, Uint32 flags); + +#define IS_GLSDL_SURFACE(s) ((s) && glSDL_GetTexInfo(s)) + +#define LOGIC_W(s) ( IS_GLSDL_SURFACE(this,s) ? TEXINFO(s)->lw : (s)->w ) +#define LOGIC_H(s) ( IS_GLSDL_SURFACE(this,s) ? TEXINFO(s)->lh : (s)->h ) + +#define GLSDL_NOTEX (~0) + +/* + * Special version for glSDL, which ignores the fake SDL_HWSURFACE + * flags, so we don't have SDL calling us back whenever we want to + * do some internal blitting... + */ +static void glSDL_SoftBlit(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect) +{ + SDL_BlitInfo info; + + if(srcrect) + if(!srcrect->w || !srcrect->h) + return; + + /* Check to make sure the blit mapping is valid */ + if ( (src->map->dst != dst) || + (src->map->dst->format_version != + src->map->format_version) ) + if ( SDL_MapSurface(src, dst) < 0 ) + return; + + /* Set up the blit information */ + if(srcrect) + { + info.s_pixels = (Uint8 *) src->pixels + + (Uint16) srcrect->y * src->pitch + + (Uint16) srcrect->x * src->format->BytesPerPixel; + info.s_width = srcrect->w; + info.s_height = srcrect->h; + } + else + { + info.s_pixels = (Uint8 *) src->pixels; + info.s_width = src->w; + info.s_height = src->h; + } + info.s_skip = src->pitch - info.s_width * src->format->BytesPerPixel; + if(dstrect) + { + info.d_pixels = (Uint8 *) dst->pixels + + (Uint16) dstrect->y * dst->pitch + + (Uint16) dstrect->x * dst->format->BytesPerPixel; + /* + * NOTE: SDL_SoftBlit() uses the 'dstrect' for this! + * This version is more like SDL_BlitSurface(). + */ + info.d_width = srcrect->w; + info.d_height = srcrect->h; + } + else + { + info.d_pixels = (Uint8 *) dst->pixels; + info.d_width = dst->w; + info.d_height = dst->h; + } + info.d_skip = dst->pitch - info.d_width * dst->format->BytesPerPixel; + info.aux_data = src->map->sw_data->aux_data; + info.src = src->format; + info.table = src->map->table; + info.dst = dst->format; + + src->map->sw_data->blit(&info); +} + + +/* + * Another special version. Doesn't lock/unlock, and doesn't mess + * with flags and stuff. It just converts the surface, period. + * Does not convert into palletized formats. + */ +static SDL_Surface *glSDL_ConvertSurface (SDL_Surface *surface, + SDL_PixelFormat *format, Uint32 flags) +{ + SDL_Surface *convert; + Uint32 colorkey = 0; + Uint8 alpha = 0; + Uint32 surface_flags; + SDL_Rect bounds; + + /* Create a new surface with the desired format */ + convert = SDL_CreateRGBSurface(flags, + surface->w, surface->h, format->BitsPerPixel, + format->Rmask, format->Gmask, format->Bmask, format->Amask); + if ( convert == NULL ) { + return(NULL); + } + + /* Save the original surface color key and alpha */ + surface_flags = surface->flags; + if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) { + /* Convert colourkeyed surfaces to RGBA if requested */ + if((flags & SDL_SRCCOLORKEY) != SDL_SRCCOLORKEY + && format->Amask) { + surface_flags &= ~SDL_SRCCOLORKEY; + } else { + colorkey = surface->format->colorkey; + SDL_SetColorKey(surface, 0, 0); + } + } + if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { + /* Copy over the alpha channel to RGBA if requested */ + if ( format->Amask ) { + surface->flags &= ~SDL_SRCALPHA; + } else { + alpha = surface->format->alpha; + SDL_SetAlpha(surface, 0, 0); + } + } + + /* Copy over the image data */ + bounds.x = 0; + bounds.y = 0; + bounds.w = surface->w; + bounds.h = surface->h; + glSDL_SoftBlit(surface, &bounds, convert, &bounds); + + /* Clean up the original surface, and update converted surface */ + if ( convert != NULL ) { + SDL_SetClipRect(convert, &surface->clip_rect); + } + if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) { + Uint32 cflags = surface_flags&(SDL_SRCCOLORKEY|SDL_RLEACCELOK); + if ( convert != NULL ) { + Uint8 keyR, keyG, keyB; + + SDL_GetRGB(colorkey,surface->format,&keyR,&keyG,&keyB); + SDL_SetColorKey(convert, cflags|(flags&SDL_RLEACCELOK), + SDL_MapRGB(convert->format, keyR, keyG, keyB)); + } + SDL_SetColorKey(surface, cflags, colorkey); + } + if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { + Uint32 aflags = surface_flags&(SDL_SRCALPHA|SDL_RLEACCELOK); + if ( convert != NULL ) { + SDL_SetAlpha(convert, aflags|(flags&SDL_RLEACCELOK), + alpha); + } + if ( format->Amask ) { + surface->flags |= SDL_SRCALPHA; + } else { + SDL_SetAlpha(surface, aflags, alpha); + } + } + + /* We're ready to go! */ + return(convert); +} + + +/*---------------------------------------------------------- + Some OpenGL function wrappers + ----------------------------------------------------------*/ + +static struct +{ + int do_blend; + int do_texture; + GLuint texture; + GLenum sfactor, dfactor; +} glstate; + +static void glSDL_reset(void) +{ + glstate.do_blend = -1; + glstate.do_blend = -1; + glstate.texture = GLSDL_NOTEX; + glstate.sfactor = 0xffffffff; + glstate.dfactor = 0xffffffff; +} + +static __inline__ void glSDL_do_blend(_THIS, int on) +{ + if(glstate.do_blend == on) + return; + + if(on) + this->glEnable(GL_BLEND); + else + this->glDisable(GL_BLEND); + glstate.do_blend = on; +} + +static __inline__ void glSDL_do_texture(_THIS, int on) +{ + if(glstate.do_texture == on) + return; + + if(on) + this->glEnable(GL_TEXTURE_2D); + else + this->glDisable(GL_TEXTURE_2D); + glstate.do_texture = on; +} + +static __inline__ void glSDL_blendfunc(_THIS, GLenum sfactor, GLenum dfactor) +{ + if((sfactor == glstate.sfactor) && (dfactor == glstate.dfactor)) + return; + + this->glBlendFunc(sfactor, dfactor); + + glstate.sfactor = sfactor; + glstate.dfactor = dfactor; +} + +static __inline__ void glSDL_texture(_THIS, GLuint tx) +{ + if(tx == glstate.texture) + return; + + this->glBindTexture(GL_TEXTURE_2D, tx); + glstate.texture = tx; +} + + + + +/*---------------------------------------------------------- + glSDL specific data types + ----------------------------------------------------------*/ + +typedef enum +{ + GLSDL_TM_SINGLE, + GLSDL_TM_HORIZONTAL, + GLSDL_TM_VERTICAL, + GLSDL_TM_HUGE +} GLSDL_TileModes; + + +typedef struct private_hwdata +{ + /* Size of surface in logic screen pixels */ + int lw, lh; + + int textures; + GLuint *texture; + int texsize; /* width/height of OpenGL texture */ + GLSDL_TileModes tilemode; + int tilew, tileh; /* At least one must equal texsize! */ + int tilespertex; + SDL_Rect virt; /* Total size of assembled surface */ + + /* Area of surface to upload when/after unlocking */ + SDL_Rect invalid_area; + + int temporary; /* Throw away after one use. */ + + SDL_Surface* next; /* The next Surface in our linked list of hardware surfaces ; == NULL if first surface */ + SDL_Surface* prev; /* The prev Surface in our linked list of hardware surfaces ; == NULL if last surface */ +} private_hwdata; + +/* some function prototypes */ +static void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area); +static void glSDL_SetLogicSize(_THIS, SDL_Surface *surface, int w, int h); +static private_hwdata *glSDL_UploadSurface(_THIS, SDL_Surface *surface); +static private_hwdata *glSDL_GetTexInfo(SDL_Surface *surface); +static void glSDL_init_formats(_THIS); +static private_hwdata *glSDL_AddTexInfo(_THIS, SDL_Surface *surface); +static void glSDL_RemoveTexInfo(_THIS, SDL_Surface *surface); +static void glSDL_UnloadTexture(_THIS, private_hwdata *txi); +static int glSDL_BlitGL(_THIS, SDL_Surface *src, + SDL_Rect *srcrect, SDL_Rect *dstrect); + +/* some variables */ +static GLint maxtexsize = -1; +static SDL_PixelFormat *RGBfmt = NULL; +static SDL_PixelFormat *RGBAfmt = NULL; +static void *mirrorbuf = NULL; +/* the raw 888 opengl surface, hidden from the application */ +SDL_Surface* OpenGL_Surface; + +/* pointer to the beggining of the list used for memory allocation */ +SDL_Surface* first = NULL; + +#ifdef DEBUG_GLSDL +static __inline__ int GLERET(const char *txt) +{ + fprintf(stderr, "glSDL ERROR: '%s'\n", txt); + return -1; +} +static __inline__ void GLERR(const char *txt) +{ + fprintf(stderr, "glSDL ERROR: '%s'\n", txt); +} +#else +#define GLERET(x) (-1) +#define GLERR(x) +#endif + +static SDL_VideoDevice underlying_device; +static int old_screen_flags; + +/* + * List of video drivers known to support OpenGL + * The purpose of this is to make glSDL "portable" across + * all video backends that support OpenGL + */ +static VideoBootStrap *opengl_bootstrap = +#if SDL_VIDEO_DRIVER_QUARTZ + &QZ_bootstrap; +#elif SDL_VIDEO_DRIVER_X11 + &X11_bootstrap; +#elif SDL_VIDEO_DRIVER_WINDIB + &WINDIB_bootstrap; +#elif SDL_VIDEO_DRIVER_BWINDOW + &BWINDOW_bootstrap; +#elif SDL_VIDEO_DRIVER_TOOLBOX + &TOOLBOX_bootstrap; +#elif SDL_VIDEO_DRIVER_CYBERGRAPHICS + &CGX_bootstrap; +#elif SDL_VIDEO_DRIVER_PHOTON + &ph_bootstrap; +#elif SDL_VIDEO_DRIVER_DC + &DC_bootstrap; +#else + NULL; +#endif + +static int glSDL_Available(void) +{ +#ifdef DEBUG_GLSDL + fprintf(stderr,"available\n"); +#endif + if (opengl_bootstrap==NULL) + return 0; + return (opengl_bootstrap->available()); +} + +static void glSDL_DeleteDevice(SDL_VideoDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +/* Create a glSDL device */ +static SDL_VideoDevice* glSDL_CreateDevice(int devindex) +{ + SDL_VideoDevice *device; +#ifdef DEBUG_GLSDL + fprintf(stderr,"entering createdevice\n"); +#endif + + /* Create the device with the underlying driver */ + device = opengl_bootstrap->create(devindex); + + /* Save the video device contents for future use */ + SDL_memcpy(&underlying_device,device,sizeof(SDL_VideoDevice)); + + /* Hook glSDL on the video device */ + device->VideoInit = glSDL_VideoInit; + device->ListModes = glSDL_ListModes; + device->VideoQuit = glSDL_VideoQuit; + device->UpdateRects = glSDL_UpdateRects; + device->FillHWRect = glSDL_FillHWRect; + device->SetHWColorKey = glSDL_SetHWColorKey; + device->SetHWAlpha = glSDL_SetHWAlpha; + device->AllocHWSurface = glSDL_AllocHWSurface; + device->LockHWSurface = glSDL_LockHWSurface; + device->UnlockHWSurface = glSDL_UnlockHWSurface; + device->FlipHWSurface = glSDL_FlipHWSurface; + device->FreeHWSurface = glSDL_FreeHWSurface; + device->CheckHWBlit = glSDL_CheckHWBlit; + device->SetColors = glSDL_SetColors; + device->SetVideoMode = glSDL_SetVideoMode; + device->info.hw_available=1; + device->info.blit_hw=1; + device->info.blit_hw_CC=1; + device->info.blit_hw_A=1; + device->info.blit_sw=1; + device->info.blit_sw_CC=1; + device->info.blit_sw_A=1; + device->info.blit_fill=1; + + /* These functions are not supported by glSDL, so we NULLify them */ + device->SetGamma = NULL; + device->GetGamma = NULL; + device->SetGammaRamp = NULL; + device->GetGammaRamp = NULL; + device->ToggleFullScreen = NULL; + + device->free = glSDL_DeleteDevice; + +#ifdef DEBUG_GLSDL + fprintf(stderr,"leaving createdevice\n"); +#endif + + return device; +} + +/* Our bootstraping structure */ +VideoBootStrap glSDL_bootstrap = { + "glSDL", "glSDL - SDL over OpenGL", + glSDL_Available, glSDL_CreateDevice +}; + +static int glSDL_VideoInit(_THIS, SDL_PixelFormat *vformat) +{ + int r; + printf("glSDL videoinit\n"); +#ifdef DEBUG_GLSDL + fprintf(stderr,"videoinit\n"); +#endif + r=underlying_device.VideoInit(this,vformat); + this->info.hw_available=1; + this->info.blit_hw=1; + this->info.blit_hw_CC=1; + this->info.blit_hw_A=1; + this->info.blit_sw=1; + this->info.blit_sw_CC=1; + this->info.blit_sw_A=1; + this->info.blit_fill=1; + + return r; +} + +SDL_Rect **glSDL_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) +{ + return((SDL_Rect **)-1); +} + +static void glSDL_VideoQuit(_THIS) +{ + SDL_Surface* scr; + + /* free all hwdata structures */ + while(first!=NULL) + glSDL_RemoveTexInfo(this, first); + + SDL_free(mirrorbuf); + mirrorbuf = NULL; + + SDL_FreeFormat(RGBfmt); + SDL_FreeFormat(RGBAfmt); + RGBfmt = RGBAfmt = NULL; + + SDL_FreeFormat(this->displayformatalphapixel); + this->displayformatalphapixel = NULL; + + SDL_FreeSurface(OpenGL_Surface); + OpenGL_Surface = NULL; + + /* restore the flags to gracefully exit from fullscreen */ + this->screen->flags = old_screen_flags; + + /* keep the screen */ + scr=this->screen; + + /* we cleaned up our stuff, now restore the underlying video driver */ + SDL_memcpy(this,&underlying_device,sizeof(SDL_VideoDevice)); + + this->screen=scr; + + /* call the underlying video driver's VideoQuit function */ + this->VideoQuit(this); +} + +static SDL_Surface* glSDL_SetVideoMode(_THIS, SDL_Surface *current,int width, int height, int bpp, Uint32 flags) +{ + SDL_Surface* hooked_screen; + int i; + int flag_doublebuf=0; + + if (opengl_bootstrap==NULL) + { + GLERR("No bootstrap for glSDL compiled in !\n"); + return NULL; + } + + /* we don't have OpenGL */ + if ((flags&SDL_INTERNALOPENGL)==SDL_INTERNALOPENGL) + { + GLERR("OpenGL video modes are not supported by glSDL !\n"); + return(NULL); + } + + /* + * Adjust the flags + */ + flags &= ~SDL_HWPALETTE; + flags |= SDL_INTERNALOPENGL; + + /* remember whether the user requested DOUBLEBUF */ + + if (flags&SDL_DOUBLEBUF) + { + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); + flag_doublebuf=1; + } + else + { + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,0); + flag_doublebuf=0; + } + + hooked_screen = underlying_device.SetVideoMode(this,current,width,height,0,flags); + + if (!hooked_screen) + { + GLERR("Unable to open an OpenGL window !\n"); + return(NULL); + } + + /* save the screen flags for restore time */ + old_screen_flags = hooked_screen->flags; + +#ifdef DEBUG_GLSDL + fprintf(stderr,"got %d bpp\n",bpp); +#endif + + /* setup the public surface format + * glSDL always returns the bpp its asked + */ + switch(bpp) + { + case 32: + this->is_32bit = 1; + this->screen = SDL_CreateRGBSurface(flags, width, height, bpp, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0x00000000 +#else + 0x0000FF00, + 0x00FF0000, + 0xFF000000, + 0x00000000 +#endif + ); + break; + case 24: + this->is_32bit = 0; + this->screen = SDL_CreateRGBSurface(flags, width, height, bpp, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0x00000000 +#else + 0x0000FF00, + 0x00FF0000, + 0xFF000000, + 0x00000000 +#endif + ); + break; + case 16: + this->is_32bit = 0; + this->screen = SDL_CreateRGBSurface(flags, width, height, bpp, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x0000F800, + 0x000007E0, + 0x0000001F, + 0x00000000 +#else + 0x0000001F, + 0x000007E0, + 0x0000F800, + 0x00000000 +#endif + ); + break; + case 15: + this->is_32bit = 0; + this->screen = SDL_CreateRGBSurface(flags, width, height, bpp, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x00007C00, + 0x000003E0, + 0x0000001F, + 0x00000000 +#else + 0x0000001F, + 0x000003E0, + 0x00007C00, + 0x00000000 +#endif + ); + break; + case 8: + default: + this->is_32bit = 0; + this->screen = SDL_CreateRGBSurface(flags, width, height, bpp, 0, 0, 0, 0); + /* give it a default palette if 8 bpp + * note : SDL already takes care of the palette for 4 bits & 1 bit surfaces + */ +/* if (bpp==8) + { + this->screen->format->palette->ncolors=255; + SDL_DitherColors(this->screen->format->palette->colors,bpp); + }*/ + break; + } + + /* also, we add SDL_HWSURFACE all the time, and let SDL create a shadow surface accordingly */ + this->screen->flags = hooked_screen->flags | SDL_HWSURFACE | SDL_INTERNALOPENGL; + /* add SDL_DOUBLEBUF if it was requested */ + if (flag_doublebuf) + this->screen->flags|=SDL_DOUBLEBUF; + + /* Tell SDL the alpha pixel format we'd like to have */ + this->displayformatalphapixel = SDL_AllocFormat(32, +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF +#else + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000 +#endif + ); + + /* Now create the raw OpenGL surface */ + OpenGL_Surface = SDL_CreateRGBSurface(flags, width, height, 24, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0x00000000 +#else + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x00000000 +#endif + ); + + /* Here we have to setup OpenGL funcs ourselves */ +#ifndef __QNXNTO__ +#define SDL_PROC(ret,func,params) \ + do { \ + this->func = SDL_GL_GetProcAddress(#func); \ + if ( ! this->func ) { \ + SDL_SetError("Couldn't load GL function: %s\n", #func); \ + return(NULL); \ + } \ + } while ( 0 ); +#else +#define SDL_PROC(ret,func,params) this->func=func; +#endif /* __QNXNTO__ */ +#include "../SDL_glfuncs.h" +#undef SDL_PROC + + if ( this->GL_MakeCurrent(this) < 0 ) + return(NULL); +#define SDL_PROC(ret,func,params) \ + do { \ + this->func = SDL_GL_GetProcAddress(#func); \ + if ( ! this->func ) { \ + SDL_SetError("Couldn't load GL function: %s\n", #func); \ + return(NULL); \ + } \ + } while ( 0 ); +#include "../SDL_glfuncs.h" +#undef SDL_PROC + + +#ifdef FAKE_MAXTEXSIZE + maxtexsize = FAKE_MAXTEXSIZE; +#else + this->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize); +#endif +#ifdef DEBUG_GLSDL + fprintf(stderr, "glSDL: Max texture size: %d\n", maxtexsize); +#endif + + glSDL_init_formats(this); + + if (flag_doublebuf) + this->glDrawBuffer(GL_BACK); + else + this->glDrawBuffer(GL_FRONT); + + this->glDisable(GL_DITHER); + + if(glSDL_AddTexInfo(this, this->screen) < 0) + { + GLERR("HookDevice() failed to add info to screen surface!"); + return NULL; + } + + glSDL_SetLogicSize(this, this->screen, + this->screen->w, this->screen->h); + + glSDL_do_texture(this, 0); + glSDL_do_blend(this, 0); + + for(i = 0; i < 1+flag_doublebuf; ++i) + { + this->glBegin(GL_TRIANGLE_FAN); + this->glColor3ub(0, 0, 0); + this->glVertex2i(0, 0); + this->glVertex2i(this->screen->w, 0); + this->glVertex2i(this->screen->w, this->screen->h); + this->glVertex2i(0, this->screen->h); + this->glEnd(); + if(!i) + this->GL_SwapBuffers(this); + } + + mirrorbuf = SDL_malloc(this->screen->h * this->screen->pitch); + if(!mirrorbuf) + { + GLERR("HookDevice() failed to allocate temp buffer for mirroring!"); + return NULL; + } + + return this->screen; +} + +static int glSDL_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) +{ + /* We don't need to fill this one */ + return 0; +} + + +#ifdef DEBUG_GLSDL +static void glSDL_print_glerror(_THIS, int point) +{ + const char *err = "<unknown>"; + switch(this->glGetError()) + { + 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"; + default: + break; + } + fprintf(stderr,"OpenGL error \"%s\" at point %d.\n", err, point); +} +#endif + +/* Get texinfo for a surface. */ +static __inline__ private_hwdata *glSDL_GetTexInfo(SDL_Surface *surface) +{ + if(!surface) + return NULL; + return surface->hwdata; +} + + +/* Allocate a "blank" texinfo for a suface. */ +static private_hwdata *glSDL_AllocTexInfo(SDL_Surface *surface) +{ + private_hwdata *txi; + if(!surface) + return NULL; + + txi = glSDL_GetTexInfo(surface); + if(txi) + return txi; /* There already is one! --> */ + + /* ...and hook a new texinfo struct up to it. */ + txi = (private_hwdata *)SDL_calloc(1, sizeof(private_hwdata)); + if(!txi) + { + GLERR("AllocTexInfo(): Failed allocating TexInfo struct!"); + return NULL; + } + txi->temporary = 1; +#ifdef DEBUG_GLSDL + fprintf(stderr, "glSDL: Allocated TexInfo %p.\n", txi); +#endif + return txi; +} + + +static void glSDL_FreeTexInfo(_THIS, private_hwdata *txi) +{ + if(!txi) + return; + + glSDL_UnloadTexture(this, txi); + SDL_free(txi->texture); + SDL_free(txi); +#ifdef DEBUG_GLSDL + fprintf(stderr, "glSDL: Freed TexInfo %p.\n", txi); +#endif +} + + +/* Detach and free the texinfo of a surface. */ +static void glSDL_RemoveTexInfo(_THIS, SDL_Surface *surface) +{ + SDL_Surface *next,*prev; + if(!glSDL_GetTexInfo(surface)) + return; + + /* maintain our doubly linked list */ + next=surface->hwdata->next; + prev=surface->hwdata->prev; + if (prev!=NULL) + { + prev->hwdata->next = next; + } + else + { + first = next; + } + if (next!=NULL) + { + next->hwdata->prev = prev; + } + + glSDL_FreeTexInfo(this, surface->hwdata); + surface->hwdata = NULL; +} + + +/* + * Calculate chopping/tiling of a surface to + * fit it into the smallest possible OpenGL + * texture. + */ +static int glSDL_CalcChop(private_hwdata *txi) +{ + int rows, vw, vh; + int vertical = 0; + int texsize; + int lastw, lasth, minsize; + + vw = txi->virt.w; + vh = txi->virt.h; + +#ifdef DEBUG_GLSDL_CHOP + fprintf(stderr, "w=%d, h=%d ", vw, vh); +#endif + if(vh > vw) + { + int t = vw; + vw = vh; + vh = t; + vertical = 1; +#ifdef DEBUG_GLSDL_CHOP + fprintf(stderr, "(vertical) \t"); +#endif + } + + /* + * 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. + */ +#ifdef DEBUG_GLSDL + if(maxtexsize < 0) + return GLERET("glSDL_CalcChop() called before OpenGL init!"); +#endif + 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 = SDL_malloc(txi->textures * sizeof(int)); + SDL_memset(txi->texture, -1, txi->textures * sizeof(int)); +#ifdef DEBUG_GLSDL + fprintf(stderr, "two-way tiling; textures=%d\n", txi->textures); +#endif + if(!txi->texture) + { + fprintf(stderr, "glSDL: 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; + } +#ifdef DEBUG_GLSDL_CHOP + fprintf(stderr, "==> minsize=%d ", minsize); + fprintf(stderr, "(rows=%d) \t", rows); +#endif + + /* Recalculate with nearest higher power-of-2 width. */ + for(texsize = 1; texsize < minsize; texsize <<= 1) + ; + txi->texsize = texsize; + rows = (vw + texsize-1) / texsize; +#ifdef DEBUG_GLSDL_CHOP + fprintf(stderr, "==> texsize=%d (rows=%d) \t", texsize, rows); +#endif + + /* Calculate number of tiles per texture */ + txi->tilespertex = txi->texsize / vh; +#ifdef DEBUG_GLSDL_CHOP + fprintf(stderr, "tilespertex=%d \t", txi->tilespertex); +#endif + + /* Calculate number of textures needed */ + txi->textures = (rows + txi->tilespertex-1) / txi->tilespertex; + txi->texture = (GLuint *)SDL_malloc(txi->textures * sizeof(GLuint)); + SDL_memset(txi->texture, GLSDL_NOTEX, txi->textures * sizeof(GLuint)); +#ifdef DEBUG_GLSDL_CHOP + fprintf(stderr, "textures=%d, ", txi->textures); +#endif + if(!txi->texture) + return GLERET("Failed to allocate texture name table!"); + + /* 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; + } + +#ifdef DEBUG_GLSDL_CHOP + fprintf(stderr, "tilew=%d, tileh=%d\n", txi->tilew, txi->tileh); +#endif + return 0; +} + + +/* Create a temporary TexInfo struct for an SDL_Surface */ +static private_hwdata *glSDL_CreateTempTexInfo(_THIS, SDL_Surface *surface) +{ + private_hwdata *txi; + if(!surface) + { + GLERR("CreateTempTexInfo(); no surface!"); + return NULL; + } + if(IS_GLSDL_SURFACE(surface)) + return glSDL_GetTexInfo(surface); /* Do nothing */ + + txi = glSDL_AllocTexInfo(surface); + if(!txi) + { + GLERR("CreateTempTexInfo(); Could not alloc TexInfo!"); + return NULL; + } + txi->virt.w = txi->lw = surface->w; + txi->virt.h = txi->lh = surface->h; + + if(glSDL_CalcChop(txi) < 0) + { + glSDL_FreeTexInfo(this, txi); + GLERR("CreateTempTexInfo(); CalcChop() failed!"); + return NULL; + } + + return txi; +} + +/* Add a glSDL_TexInfo struct to an SDL_Surface */ +static private_hwdata *glSDL_AddTexInfo(_THIS, SDL_Surface *surface) +{ + private_hwdata *txi = glSDL_CreateTempTexInfo(this, surface); + if(!txi) + return NULL; + + /* Connect the surface to the new TexInfo. */ + txi->temporary = 0; + surface->hwdata = txi; + + /* add this new surface in front of the list of hw surfaces */ + txi->next = first; + txi->prev = NULL; + first = surface; + if (txi->next!=NULL) + { + txi->next->hwdata->prev=surface; + } + + SDL_SetClipRect(surface, &txi->virt); + return txi; +} + + +/* Create a surface of the prefered OpenGL RGB texture format */ +/*static SDL_Surface *glSDL_CreateRGBSurface(int w, int h) +{ + SDL_Surface *s; + Uint32 rmask, gmask, bmask; + int bits = 24; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + rmask = 0x000000FF; + gmask = 0x0000FF00; + bmask = 0x00FF0000; +#else + rmask = 0x00FF0000; + gmask = 0x0000FF00; + bmask = 0x000000FF; +#endif + s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, + bits, rmask, gmask, bmask, 0); + if(s) + s->flags |= SDL_HWACCEL; + + return s; +} +*/ + +/* Create a surface of the prefered OpenGL RGBA texture format */ +static SDL_Surface *glSDL_CreateRGBASurface(int w, int h) +{ + SDL_Surface *s; + Uint32 rmask, gmask, bmask, amask; + int bits = 32; +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + rmask = 0x000000FF; + gmask = 0x0000FF00; + bmask = 0x00FF0000; + amask = 0xFF000000; +#else + rmask = 0xFF000000; + gmask = 0x00FF0000; + bmask = 0x0000FF00; + amask = 0x000000FF; +#endif + s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, + bits, rmask, gmask, bmask, amask); + if(s) + s->flags |= SDL_HWACCEL; + + return s; +} + + +static void glSDL_init_formats(_THIS) +{ + RGBfmt = SDL_AllocFormat(24, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0); +#else + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0); +#endif + RGBAfmt = SDL_AllocFormat(32, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000FF, + 0x0000FF00, + 0x00FF0000, + 0xFF000000); +#else + 0xFF000000, + 0x00FF0000, + 0x0000FF00, + 0x000000FF); +#endif +} + + +static int glSDL_FormatIsOk(SDL_Surface *surface) +{ + SDL_PixelFormat *pf; + if(!surface) + return 1; /* Well, there ain't much we can do anyway... */ + + pf = surface->format; + + /* Colorkeying requires an alpha channel! */ + if(surface->flags & SDL_SRCCOLORKEY) + if(!pf->Amask) + 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 glSDL_key2alpha(SDL_Surface *surface) +{ + int x, y; + Uint32 ckey = surface->format->colorkey; + +#ifdef DEBUG_GLSDL + fprintf(stderr,"glSDL_key2alpha()\n"); +#endif + 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] == ckey) + px[x] = 0; + } +} + + + +/*---------------------------------------------------------- + SDL style API + ----------------------------------------------------------*/ + +static int glSDL_FlipHWSurface(_THIS, SDL_Surface *surface) +{ +#ifdef GLSDL_GRAPHICAL_DEBUG + this->glDisable(GL_TEXTURE_2D); + this->glBegin(GL_LINE_LOOP); + this->glColor4ub(0, 0, 255, 128); + this->glVertex2i(0,0); + this->glVertex2i(surface->w,0); + this->glVertex2i(surface->w,surface->h); + this->glVertex2i(0,surface->h); + this->glEnd(); + this->glEnable(GL_TEXTURE_2D); +#endif + if (this->screen->flags&SDL_DOUBLEBUF) + this->GL_SwapBuffers(this); + else + this->glFinish(); + return 0; +} + + +static void glSDL_UpdateRects(_THIS, int numrects, SDL_Rect *rects) +{ +#ifdef GLSDL_GRAPHICAL_DEBUG + int i; + this->glDisable(GL_TEXTURE_2D); + for(i=0;i<numrects;i++) + { + this->glColor4ub(255, 0, 0, 128); + this->glBegin(GL_LINE_LOOP); + this->glVertex2i(rects[i].x,rects[i].y); + this->glVertex2i(rects[i].x+rects[i].w,rects[i].y); + this->glVertex2i(rects[i].x+rects[i].w,rects[i].y+rects[i].h); + this->glVertex2i(rects[i].x,rects[i].y+rects[i].h); + this->glEnd(); + } + this->glEnable(GL_TEXTURE_2D); +#endif + if (this->screen->flags&SDL_DOUBLEBUF) + this->GL_SwapBuffers(this); + else + this->glFinish(); +} + + +static int glSDL_AllocHWSurface(_THIS, SDL_Surface *surface) +{ + surface->flags |= (SDL_HWSURFACE|SDL_HWACCEL); + + surface->pixels = SDL_malloc(surface->h*surface->pitch); + if ( surface->pixels == NULL ) { + SDL_FreeSurface(surface); + SDL_OutOfMemory(); + return(-1); + } + SDL_memset(surface->pixels, 0, surface->h*surface->pitch); + return 0; +} + + +static void glSDL_FreeHWSurface(_THIS, SDL_Surface *surface) +{ + if(!surface) + return; + glSDL_RemoveTexInfo(this, surface); +} + + +static int glSDL_LockHWSurface(_THIS, SDL_Surface *surface) +{ + int y; + + if(!surface) + return -1; + +#ifdef DEBUG_GLSDL + fprintf(stderr, "glSDL: Lock Surface.\n"); +#endif + + if(SDL_VideoSurface == surface) + { + glSDL_Invalidate(surface, NULL); + this->glPixelStorei(GL_UNPACK_ROW_LENGTH, + surface->pitch / + surface->format->BytesPerPixel); + this->glReadPixels(0, 0, OpenGL_Surface->w, OpenGL_Surface->h, + GL_RGB, + GL_UNSIGNED_BYTE, + OpenGL_Surface->pixels); + for(y = 0; y < OpenGL_Surface->h / 2; ++y) + { + void *upper = (Uint8 *)OpenGL_Surface->pixels + + OpenGL_Surface->pitch * y; + void *lower = (Uint8 *)OpenGL_Surface->pixels + + OpenGL_Surface->pitch * (OpenGL_Surface->h - y - 1); + SDL_memcpy(mirrorbuf, upper, OpenGL_Surface->pitch); + SDL_memcpy(upper, lower, OpenGL_Surface->pitch); + SDL_memcpy(lower, mirrorbuf, OpenGL_Surface->pitch); + } + /* the mapping has to be invalidated on 8bpp video surfaces in case of a hw palette change. + * Now if someone could tell me why this is not handled by SDL... */ + if (SDL_VideoSurface->format->BitsPerPixel==8) + SDL_InvalidateMap(OpenGL_Surface->map); + + /* convert this raw surface to the application-requested format + * FIXME this is sometimes overkill, we could use glPixelStore smartly + * But this would be slow anyway :) */ + + glSDL_SoftBlit(OpenGL_Surface, NULL, SDL_VideoSurface, NULL); + } + else + glSDL_Invalidate(surface, NULL); + + return 0; +} + + +static void glSDL_UnlockHWSurface(_THIS, SDL_Surface *surface) +{ + private_hwdata *txi; + + if(!surface) + return; + + /* upload this surface ONLY if this is a glSDL surface + * because sometimes (during displayformating for ex.) surfaces are unlocked that aren't glSDL + */ + if(!IS_GLSDL_SURFACE(surface)) + return; + +#ifdef DEBUG_GLSDL + fprintf(stderr, "glSDL: Unlock Surface.\n"); +#endif + + txi = glSDL_UploadSurface(this, surface); + + if(!txi) + { + GLERR("glSDL_UnlockHWSurface() failed to upload surface!"); + return; + } + if(txi->temporary) + { + GLERR("Weirdness... glSDL_UnlockHWSurface() got a temporary TexInfo."); + return; + } + if(surface == SDL_VideoSurface) + glSDL_BlitGL(this, SDL_VideoSurface, NULL, NULL); +} + + +static int glSDL_SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key) +{ + /* + * 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_RemoveTexInfo(this, surface); + return 0; +} + + +static int glSDL_SetHWAlpha(_THIS, SDL_Surface *surface, Uint8 alpha) +{ + /* + * 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_RemoveTexInfo(this, surface); + return 0; +} + +static SDL_bool glSDL_SetClipRect(_THIS, SDL_Surface *surface, SDL_Rect *rect) +{ + SDL_bool res; + if(!surface) + return SDL_FALSE; + + res = SDL_SetClipRect(surface, rect); + if(!res) + return SDL_FALSE; + + rect = &surface->clip_rect; + + if(surface == SDL_VideoSurface) + { + SDL_Rect r; + float xscale, yscale; + private_hwdata *txi; + + r.x = rect->x; + r.y = rect->y; + r.w = rect->w; + r.h = rect->h; + SDL_SetClipRect(surface, rect); + + txi = glSDL_GetTexInfo(surface); + if(!txi) + return GLERET("SetClipRect(): Could not get TexInfo!"); + + this->glViewport(rect->x, + surface->h - (rect->y + rect->h), + rect->w, + rect->h); + /* + * Note that this projection is upside down in + * relation to the OpenGL coordinate system. + */ + this->glMatrixMode(GL_PROJECTION); + this->glLoadIdentity(); + xscale = (float)txi->lw / (float)surface->w; + yscale = (float)txi->lh / (float)surface->h; + this->glOrtho( xscale*(float)rect->x, + xscale*(float)(rect->w+rect->x), + yscale*(float)(rect->h+rect->y), + yscale*(float)rect->y, + -1.0, 1.0); + return SDL_TRUE; + } + return res; +} + +static int glSDL_BlitFromGL(_THIS, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect) +{ + SDL_Rect sr, dr; + + /* In case the destination has an OpenGL texture... */ + glSDL_Invalidate(dst, dstrect); + + /* Abuse the fake screen buffer a little. */ + this->glPixelStorei(GL_UNPACK_ROW_LENGTH, SDL_VideoSurface->pitch / + SDL_VideoSurface->format->BytesPerPixel); + if(srcrect) + this->glReadPixels(srcrect->x, OpenGL_Surface->h - (srcrect->y + srcrect->h - 1), + srcrect->w, srcrect->h, + GL_RGB, GL_UNSIGNED_BYTE, OpenGL_Surface->pixels); + else + this->glReadPixels(0, 0, OpenGL_Surface->w, OpenGL_Surface->h, + GL_RGB, GL_UNSIGNED_BYTE, OpenGL_Surface->pixels); + sr = *srcrect; + dr = *dstrect; + glSDL_SoftBlit(OpenGL_Surface, &sr, dst, &dr); + return 0; +} + +static __inline__ void glSDL_BlitGL_single(_THIS, private_hwdata *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; + glSDL_texture(this, 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; + +#ifdef GLSDL_GRAPHICAL_DEBUG + this->glDisable(GL_TEXTURE_2D); + this->glBegin(GL_LINE_LOOP); + this->glColor4ub(0, 255, 0, 128); + this->glVertex2i(dst->x, dst->y); + this->glVertex2i(dst->x + dst->w, dst->y); + this->glVertex2i(dst->x + dst->w, dst->y + dst->h); + this->glVertex2i(dst->x, dst->y + dst->h); + this->glEnd(); + this->glEnable(GL_TEXTURE_2D); +#endif + + this->glBegin(GL_TRIANGLE_FAN); + this->glColor4ub(255, 255, 255, alpha); + this->glTexCoord2f(sx1, sy1); + this->glVertex2i(dst->x, dst->y); + this->glTexCoord2f(sx2, sy1); + this->glVertex2i(dst->x + dst->w, dst->y); + this->glTexCoord2f(sx2, sy2); + this->glVertex2i(dst->x + dst->w, dst->y + dst->h); + this->glTexCoord2f(sx1, sy2); + this->glVertex2i(dst->x, dst->y + dst->h); + this->glEnd(); +} + + +static void glSDL_BlitGL_htile(_THIS, private_hwdata *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; + glSDL_texture(this, txi->texture[tex]); + + 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 -= tsx1 * txi->texsize; + tsx1 = 0.0; + } + if(tsx2 > 1.0) + { + tdx2 -= (tsx2 - 1.0) * txi->texsize; + tsx2 = 1.0; + } + + /* Maybe select next texture? */ + if(yo + tileh > 1.0) + { + ++tex; + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + glSDL_texture(this, txi->texture[tex]); + yo = 0.0; + } + +#ifdef GLSDL_GRAPHICAL_DEBUG + this->glDisable(GL_TEXTURE_2D); + this->glBegin(GL_LINE_LOOP); + this->glColor4ub(0, 255, 0, 128); + this->glVertex2i(tdx1, dst->y); + this->glVertex2i(tdx2, dst->y); + this->glVertex2i(tdx2, dst->y + dst->h); + this->glVertex2i(tdx1, dst->y + dst->h); + this->glEnd(); + this->glEnable(GL_TEXTURE_2D); +#endif + + this->glBegin(GL_TRIANGLE_FAN); + this->glColor4ub(255, 255, 255, alpha); + this->glTexCoord2f(tsx1, yo + sy1); + this->glVertex2i(tdx1, dst->y); + this->glTexCoord2f(tsx2, yo + sy1); + this->glVertex2i(tdx2, dst->y); + this->glTexCoord2f(tsx2, yo + sy2); + this->glVertex2i(tdx2, dst->y + dst->h); + this->glTexCoord2f(tsx1, yo + sy2); + this->glVertex2i(tdx1, dst->y + dst->h); + this->glEnd(); + tile += 1.0; + yo += tileh; + } +} + + +static void glSDL_BlitGL_vtile(_THIS, private_hwdata *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; + glSDL_texture(this, txi->texture[tex]); + + 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 -= tsy1 * txi->texsize; + tsy1 = 0.0; + } + if(tsy2 > 1.0) + { + tdy2 -= (tsy2 - 1.0) * txi->texsize; + tsy2 = 1.0; + } + + /* Maybe select next texture? */ + if(xo + tilew > 1.0) + { + ++tex; + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + glSDL_texture(this, txi->texture[tex]); + xo = 0.0; + } + +#ifdef GLSDL_GRAPHICAL_DEBUG + this->glDisable(GL_TEXTURE_2D); + this->glBegin(GL_LINE_LOOP); + this->glColor4ub(0, 255, 0, 128); + this->glVertex2i(dst->x, tdy1); + this->glVertex2i(dst->x + dst->w, tdy1); + this->glVertex2i(dst->x + dst->w, tdy2); + this->glVertex2i(dst->x, tdy2); + this->glEnd(); + this->glEnable(GL_TEXTURE_2D); +#endif + + this->glBegin(GL_TRIANGLE_FAN); + this->glColor4ub(255, 255, 255, alpha); + this->glTexCoord2f(xo + sx1, tsy1); + this->glVertex2i(dst->x, tdy1); + this->glTexCoord2f(xo + sx2, tsy1); + this->glVertex2i(dst->x + dst->w, tdy1); + this->glTexCoord2f(xo + sx2, tsy2); + this->glVertex2i(dst->x + dst->w, tdy2); + this->glTexCoord2f(xo + sx1, tsy2); + this->glVertex2i(dst->x, tdy2); + this->glEnd(); + + tile += 1.0; + xo += tilew; + } +} + + +static void glSDL_BlitGL_hvtile(_THIS, SDL_Surface *src, private_hwdata *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 = floor(sy1) * tilesperrow + floor(sx1); + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + glSDL_texture(this, txi->texture[tex]); + + for(y = 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 -= tsy1 * txi->texsize; + tsy1 = 0.0; + } + if(tsy2 > 1.0) + { + tdy2 -= (tsy2 - 1.0) * txi->texsize; + tsy2 = 1.0; + } + for(x = 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 -= tsx1 * txi->texsize; + tsx1 = 0.0; + } + if(tsx2 > 1.0) + { + tdx2 -= (tsx2 - 1.0) * txi->texsize; + tsx2 = 1.0; + } + + /* Select texture */ + tex = y * tilesperrow + x; + if(tex != last_tex) + { + if(tex >= txi->textures) + return; + if(-1 == txi->texture[tex]) + return; + glSDL_texture(this, txi->texture[tex]); + last_tex = tex; + } + +#ifdef GLSDL_GRAPHICAL_DEBUG + this->glDisable(GL_TEXTURE_2D); + this->glBegin(GL_LINE_LOOP); + this->glColor4ub(0, 255, 0, 128); + this->glVertex2i(tdx1, tdy1); + this->glVertex2i(tdx2, tdy1); + this->glVertex2i(tdx2, tdy2); + this->glVertex2i(tdx1, tdy2); + this->glEnd(); + this->glEnable(GL_TEXTURE_2D); +#endif + + this->glBegin(GL_TRIANGLE_FAN); + this->glColor4ub(255, 255, 255, alpha); + this->glTexCoord2f(tsx1, tsy1); + this->glVertex2i(tdx1, tdy1); + this->glTexCoord2f(tsx2, tsy1); + this->glVertex2i(tdx2, tdy1); + this->glTexCoord2f(tsx2, tsy2); + this->glVertex2i(tdx2, tdy2); + this->glTexCoord2f(tsx1, tsy2); + this->glVertex2i(tdx1, tdy2); + this->glEnd(); + } + } +} + +/* + * 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); + + /* 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(_THIS, SDL_Surface *src, + SDL_Rect *srcrect, SDL_Rect *dstrect) +{ + private_hwdata *txi; + float x1, y1; + unsigned char alpha; + SDL_Rect d; + int x, y; + SDL_Rect r; + + if(!src) + return GLERET("BlitGL(): No src surface!"); + + /* 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, &this->screen->clip_rect)) + { + 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 */ + txi = glSDL_UploadSurface(this, src); + if(!txi) + return GLERET("BlitGL(): Could not get a TexInfo!"); + + /* Set up blending */ + if(src->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY)) + { + glSDL_blendfunc(this, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glSDL_do_blend(this, 1); + } + else + glSDL_do_blend(this, 0); + + /* Enable texturing */ + glSDL_do_texture(this, 1); + + /* Calculate texcoords */ + if(!srcrect) + srcrect = &txi->virt; + x1 = (float)srcrect->x; + y1 = (float)srcrect->y; + + /* Calculate screen coords. */ + if(dstrect) + { + d.x = dstrect->x; + d.y = dstrect->y; + d.w = (int)(srcrect->w * (float)txi->lw / (float)txi->virt.w); + d.h = (int)(srcrect->h * (float)txi->lh / (float)txi->virt.h); + } + else + { + d.x = 0; + d.y = 0; + d.w = (int)(srcrect->w * (float)txi->lw / (float)txi->virt.w); + d.h = (int)(srcrect->h * (float)txi->lh / (float)txi->virt.h); + } + + /* + * 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)) + alpha = 255; + else + alpha = src->format->alpha; + + /* Render! */ + switch(txi->tilemode) + { + case GLSDL_TM_SINGLE: + glSDL_BlitGL_single(this, txi, x1, y1, + &d, + alpha); + break; + case GLSDL_TM_HORIZONTAL: + glSDL_BlitGL_htile(this, txi, x1, y1, + &d, + alpha); + break; + case GLSDL_TM_VERTICAL: + glSDL_BlitGL_vtile(this, txi, x1, y1, + &d, + alpha); + break; + case GLSDL_TM_HUGE: + glSDL_BlitGL_hvtile(this, src, txi, x1, y1, + &d, + alpha); + break; + } + + if(txi->temporary) + glSDL_FreeTexInfo(this, txi); + + return 0; +} + + +static int glSDL_HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, + SDL_Surface *dst, SDL_Rect *dstrect) +{ + SDL_Surface *vs; + + if(!src) + return GLERET("HWAccelBlit(): No src surface!"); + if(!dst) + return GLERET("HWAccelBlit(): No dst surface!"); + + /* + * Figure out what to do: + * screen->screen: glSDL_BlitFromGL() + glSDL_BlitGL() + * surface->screen: glSDL_BlitGL() + * screen->surface: glSDL_BlitFromGL() + * surface->surface: glSDL_SoftBlit() + */ + vs = SDL_VideoSurface; + if(src == vs) + { + if(dst == vs) + { + /* +FIXME: Try glCopyPixels() instead... + */ + glSDL_BlitFromGL(current_video, srcrect, vs, dstrect); + return glSDL_BlitGL(current_video, vs, + srcrect, dstrect); + } + else + { + return glSDL_BlitFromGL(current_video, srcrect, + dst, dstrect); + } + } + else + { + if(dst == vs) + { + return glSDL_BlitGL(current_video, src, + srcrect, dstrect); + } + else + { + glSDL_Invalidate(dst, dstrect); + glSDL_SoftBlit(src, srcrect, dst, dstrect); + return 0; + } + } +} + + +static int glSDL_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color) +{ + SDL_Surface *vs = SDL_VideoSurface; + int dx1, dy1, dx2, dy2; + Uint32 r, g, b; + Uint8 br,bg,bb; + + /* + * Some ugly reverse conversion for compatibility... + * (We must do this before losing the dst pointer, + * as the pixel formats of the screen and + * SDL_VideoSurface may differ!) + */ + + if (dst->format->palette) + { + /* this a paletted color */ + SDL_GetRGB(color,dst->format,&br,&bg,&bb); + } + else + { + /* this a RGB color */ + r = color & dst->format->Rmask; + r = r >> dst->format->Rshift; + r = r << dst->format->Rloss; + br = r; + + g = color & dst->format->Gmask; + g = g >> dst->format->Gshift; + g = g << dst->format->Gloss; + bg = g; + + b = color & dst->format->Bmask; + b = b >> dst->format->Bshift; + b = b << dst->format->Bloss; + bb = b; + } + + if(vs != dst) + { + /* draw a rect offscreen */ + glSDL_Invalidate(dst, dstrect); + /* software-fill the surface by faking it as a SW_SURFACE */ + dst->flags&=~SDL_HWSURFACE; + SDL_FillRect(dst,dstrect,color); + dst->flags|=SDL_HWSURFACE; + } + else + { + /* draw a rect onscreen */ + glSDL_do_texture(this, 0); + glSDL_do_blend(this, 0); + + dx1 = dstrect->x; + dy1 = dstrect->y; + dx2 = dx1 + dstrect->w; + dy2 = dy1 + dstrect->h; + + this->glBegin(GL_TRIANGLE_FAN); + this->glColor3ub(br, bg, bb); + this->glVertex2i(dx1, dy1); + this->glVertex2i(dx2, dy1); + this->glVertex2i(dx2, dy2); + this->glVertex2i(dx1, dy2); + this->glEnd(); + } + return 0; +} + +static int glSDL_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dst) +{ + src->flags |= SDL_HWACCEL; + src->map->hw_blit = glSDL_HWAccelBlit; + return 1; +} + + +static SDL_Surface *glSDL_DisplayFormat(SDL_Surface *surface) +{ + SDL_Surface *tmp; + int use_rgba = (surface->flags & SDL_SRCCOLORKEY) || + ((surface->flags & SDL_SRCALPHA) && + surface->format->Amask); +#ifdef DEBUG_GLSDL + fprintf(stderr,"#### glSDL_DisplayFormat()\n"); +#endif + if(use_rgba) + tmp = glSDL_ConvertSurface(surface, RGBAfmt, SDL_SWSURFACE); + else + tmp = glSDL_ConvertSurface(surface, RGBfmt, SDL_SWSURFACE); + if(!tmp) + { + GLERR("glSDL_DisplayFormat() could not convert surface!"); + return NULL; + } + 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); + glSDL_key2alpha(tmp); + SDL_SetColorKey(tmp, 0, 0); + } + + return tmp; +} + + +static SDL_Surface *glSDL_DisplayFormatAlpha(SDL_Surface *surface) +{ + SDL_Surface *s, *tmp; + tmp = glSDL_ConvertSurface(surface, RGBAfmt, SDL_SWSURFACE); +#ifdef DEBUG_GLSDL + fprintf(stderr,"#### glSDL_DisplayFormatAlpha()\n"); +#endif + if(!tmp) + return NULL; + + SDL_SetAlpha(tmp, 0, 0); + SDL_SetColorKey(tmp, 0, 0); + s = glSDL_CreateRGBASurface(surface->w, surface->h); + if(!s) + { + SDL_FreeSurface(tmp); + return NULL; + } + glSDL_SoftBlit(tmp, NULL, s, NULL); + SDL_FreeSurface(tmp); + + if(surface->flags & SDL_SRCCOLORKEY) + { + SDL_SetColorKey(s, SDL_SRCCOLORKEY, + surface->format->colorkey); + glSDL_key2alpha(s); + SDL_SetColorKey(s, 0, 0); + } + + if(surface->flags & SDL_SRCALPHA) + SDL_SetAlpha(s, SDL_SRCALPHA, + surface->format->alpha); + return s; +} + + +/*---------------------------------------------------------- + glSDL specific API extensions + ----------------------------------------------------------*/ + +static void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area) +{ + private_hwdata *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; +} + + +static void glSDL_SetLogicSize(_THIS, SDL_Surface *surface, int w, int h) +{ + SDL_Rect r; + private_hwdata *txi; + if(!IS_GLSDL_SURFACE(surface)) + return; + + txi = glSDL_GetTexInfo(surface); + + txi->lw = w; + txi->lh = h; + + if(SDL_VideoSurface != surface) + return; + + r.x = r.y = 0; + r.w = w; + r.h = h; + glSDL_SetClipRect(this, surface, &r); + + this->glMatrixMode(GL_MODELVIEW); + this->glLoadIdentity(); + this->glTranslated(0.0f, 0.0f, 0.0f); + + this->glDisable(GL_DEPTH_TEST); + this->glDisable(GL_CULL_FACE); + + glSDL_reset(); +} + +static int glSDL_InitTexture(_THIS, SDL_Surface *datasurf, private_hwdata *txi, int tex) +{ + this->glGenTextures(1, (GLuint *)&txi->texture[tex]); + this->glBindTexture(GL_TEXTURE_2D, txi->texture[tex]); + glstate.texture = txi->texture[tex]; + this->glPixelStorei(GL_UNPACK_ROW_LENGTH, datasurf->pitch / + datasurf->format->BytesPerPixel); + this->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + this->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + this->glTexImage2D(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); +#ifdef DEBUG_GLSDL + glSDL_print_glerror(this, 1); +#endif + return 0; +} + + +/* Image tiled horizontally (wide surface), or not at all */ +static int glSDL_UploadHoriz(_THIS, SDL_Surface *datasurf, private_hwdata *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 = glSDL_InitTexture(this, datasurf, txi, tex); + if(res < 0) + return res; + ++tex; + } + this->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, toy, + thistw, txi->tileh, + datasurf->format->Amask ? GL_RGBA : GL_RGB, + GL_UNSIGNED_BYTE, + (char *)datasurf->pixels + bpp * fromx); +#ifdef DEBUG_GLSDL + glSDL_print_glerror(this, 2); +#endif + fromx += txi->tilew; + toy += txi->tileh; + } + return 0; +} + + +/* Image tiled vertically (tall surface) */ +static int glSDL_UploadVert(_THIS, SDL_Surface *datasurf, private_hwdata *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 = glSDL_InitTexture(this, datasurf, txi, tex); + if(res < 0) + return res; + ++tex; + } + this->glTexSubImage2D(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); +#ifdef DEBUG_GLSDL + glSDL_print_glerror(this, 3); +#endif + fromy += txi->tileh; + tox += txi->tilew; + } + return 0; +} + + +/* Image tiled two-way (huge surface) */ +static int glSDL_UploadHuge(_THIS, SDL_Surface *datasurf, private_hwdata *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 = glSDL_InitTexture(this, datasurf, txi, tex++); + if(res < 0) + return res; + this->glTexSubImage2D(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); +#ifdef DEBUG_GLSDL + fprintf(stderr,"glTexSubImage(x = %d, y = %d, w = %d, h = %d)\n", + x, y, thistw, thisth); + glSDL_print_glerror(this, 4); +#endif + x += txi->tilew; + } + y += txi->tileh; + } + return 0; +} + + +/* Upload all textures for a surface. */ +static int glSDL_UploadTextures(_THIS, SDL_Surface *datasurf, private_hwdata *txi) +{ + switch(txi->tilemode) + { + case GLSDL_TM_SINGLE: + case GLSDL_TM_HORIZONTAL: + glSDL_UploadHoriz(this, datasurf, txi); + break; + case GLSDL_TM_VERTICAL: + glSDL_UploadVert(this, datasurf, txi); + break; + case GLSDL_TM_HUGE: + glSDL_UploadHuge(this, datasurf, txi); + break; + } + return 0; +} + + +/* + * IMPORTANT: + * This function will try various ways of giving you + * a TexInfo, and will succeed most of the time. + * + * However, the TexInfo returned may be temporary, + * (as opposed to connected to 'surface'). A temporary + * TexInfo must be used only once and then thrown away, + * since it means that glSDL cannot track changes in + * the pixel data of 'texture'. + */ +static private_hwdata *glSDL_UploadSurface(_THIS, SDL_Surface *surface) +{ + int i; + int converted = 0; + private_hwdata *txi = glSDL_GetTexInfo(surface); + + if(IS_GLSDL_SURFACE(surface)) + { + /* + * Ok, this is a glSDL surface, and it *might* be + * in texture memory already. If so, it may need + * an update. + */ + if(txi->invalid_area.w) + { + glSDL_UnloadTexture(this, txi); + } + else + { + int missing = 0; + if(txi->textures) + { + for(i = 0; i < txi->textures; ++i) + if(GLSDL_NOTEX == txi->texture[i]) + { + missing = 1; + break; + } + if(!missing) + return txi; /* They're already there! */ + } + } + } + else + { + /* + * Nope, this isn't (yet) a glSDL surface. Let's + * try to either make it one, or set up a temporary + * TexInfo for it, valid for only one blit. + */ + if( (surface->flags & SDL_HWSURFACE) == SDL_HWSURFACE ) + { + txi = glSDL_AddTexInfo(this, surface); + if(!txi) + { + GLERR("UploadSurface(): Could not add TexInfo!"); + return NULL; + } + surface->flags |= SDL_HWSURFACE; + surface->flags |= SDL_HWACCEL; + } + else + { + /* + * FIXME + * here if the surface is small enough, it's a good + * candidate for a blit using glDrawPixels instead + * of a texture blit + */ + txi = glSDL_CreateTempTexInfo(this, surface); + if(!txi) + { + GLERR("UploadSurface(): Could not create temp TexInfo!"); + return NULL; + } + } + } + + if(txi->texsize > maxtexsize) + { + /* This surface wasn't tiled properly... */ + if(txi->temporary) + glSDL_FreeTexInfo(this, txi); + GLERR("UploadSurface(): Too large texture!"); + return NULL; + } + + /* + * 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(!glSDL_FormatIsOk(surface)) + { +#ifdef DEBUG_GLSDL + fprintf(stderr, "glSDL: WARNING: On-the-fly conversion performed!\n"); +#endif + converted = 1; + /* NOTE: We forget about the original surface here. */ + if(surface->format->Amask) + surface = glSDL_DisplayFormatAlpha(surface); + else + surface = glSDL_DisplayFormat(surface); + if(!surface) + { + GLERR("UploadSurface(): Could not convert surface!"); + if(txi->temporary) + glSDL_FreeTexInfo(this, txi); + return NULL; + } + } + + glSDL_UploadTextures(this, surface, txi); + + if(converted) + SDL_FreeSurface(surface); + + return txi; +} + + +static void glSDL_UnloadTexture(_THIS, private_hwdata *txi) +{ + int i; + for(i = 0; i < txi->textures; ++i) + if(txi->texture[i] != GLSDL_NOTEX) + this->glDeleteTextures(1, &txi->texture[i]); + SDL_memset(&txi->invalid_area, 0, sizeof(txi->invalid_area)); +}