view 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 source

/*
    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));
}