Mercurial > sdl-ios-xcode
diff src/video/glsdl/SDL_glsdl.c @ 1895:c121d94672cb
SDL 1.2 is moving to a branch, and SDL 1.3 is becoming the head.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Mon, 10 Jul 2006 21:04:37 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/video/glsdl/SDL_glsdl.c Mon Jul 10 21:04:37 2006 +0000 @@ -0,0 +1,2465 @@ +/* + 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)); +} + +/* vi: set ts=4 sw=4 expandtab: */