1 /*(LGPL)
2 ------------------------------------------------------------
3 	glSDL 0.6 - SDL 2D API on top of OpenGL
4 ------------------------------------------------------------
5  * (c) David Olofson, 2001-2003
6  * This code is released under the terms of the GNU LGPL.
7  */
8 
9 #define	_GLSDL_NO_REDEFINES_
10 #include "glSDL.h"
11 
12 #ifdef HAVE_OPENGL
13 
14 /*
15  * Note that this will result in whining about
16  * TexInfo 0 being leaked, as the checking is
17  * done before the screen is closed. Ignore. :-)
18  */
19 /* #define LEAK_TRACKING */
20 
21 #define	DBG(x)  	/*error messages, warnings*/
22 #define	DBG2(x)		/*texture allocation*/
23 #define	DBG3(x)		/*chopping/tiling*/
24 #define	DBG4(x)		/*texture uploading*/
25 
26 /*#define	CKSTATS*/		/*colorkey statistics*/
27 /*#define	FAKE_MAXTEXSIZE	256*/
28 
29 #include <string.h>
30 #include <stdlib.h>
31 #include <math.h>
32 
33 #define HAS_SDL_OPENGL_H 1
34 
35 #if HAS_SDL_OPENGL_H
36 #include "SDL_opengl.h"
37 #else
38 #ifdef WIN32
39 #include <windows.h>
40 #endif
41 #if defined(__APPLE__) && defined(__MACH__)
42 #include <OpenGL/gl.h>
43 #include <OpenGL/glu.h>
44 #else
45 #include <GL/gl.h>
46 #include <GL/glu.h>
47 #endif
48 #endif
49 
50 #define	USING_GLSDL	(IS_GLSDL_SURFACE(SDL_GetVideoSurface()))
51 
52 #define	MAX_TEXINFOS	16384
53 
54 static glSDL_TexInfo **texinfotab = NULL;
55 static GLint maxtexsize = 256;
56 static SDL_PixelFormat _RGBfmt, _RGBAfmt;
57 static int screen_was_locked = 0;
58 
59 static void _UnloadTexture(glSDL_TexInfo *txi);
60 
61 static int scale = 1;
62 
63 static SDL_Surface *fake_screen = NULL;
64 
65 
66 static int _glSDL_BlitGL(SDL_Surface *src, SDL_Rect *srcrect,
67 			 SDL_Surface *dst, SDL_Rect *dstrect);
68 
69 
DBG(static void _print_glerror (int point){ const char *err = "<unknown>"; switch(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"; break; default: err = "<unknown>"; break; } fprintf(stderr,"OpenGL error \\"%s\\" at point %d.\\n", err, point); })70 DBG(static void _print_glerror(int point)
71 {
72 	const char *err = "<unknown>";
73 	switch(glGetError())
74 	{
75 	  case GL_NO_ERROR:
76 		return;
77 	  case GL_INVALID_ENUM:
78 		err = "GL_INVALID_ENUM";
79 		break;
80 	  case GL_INVALID_VALUE:
81 		err = "GL_INVALID_VALUE";
82 		break;
83 	  case GL_INVALID_OPERATION:
84 		err = "GL_INVALID_OPERATION";
85 		break;
86 	  case GL_STACK_OVERFLOW:
87 		err = "GL_STACK_OVERFLOW";
88 		break;
89 	  case GL_STACK_UNDERFLOW:
90 		err = "GL_STACK_UNDERFLOW";
91 		break;
92 	  case GL_OUT_OF_MEMORY:
93 		err = "GL_OUT_OF_MEMORY";
94 		break;
95 	  default:
96 		err = "<unknown>";
97 		break;
98 	}
99 	fprintf(stderr,"OpenGL error \"%s\" at point %d.\n", err, point);
100 })
101 
102 
103 /* Get texinfo for a surface. */
104 glSDL_TexInfo *glSDL_GetTexInfo(SDL_Surface *surface)
105 {
106 	if(!surface)
107 		return NULL;
108 	if(!texinfotab)
109 		return NULL;
110 	if(!surface->unused1)
111 		return NULL;
112 	if(surface->unused1 > MAX_TEXINFOS)
113 		return NULL;
114 
115 	return texinfotab[surface->unused1 - 1];
116 }
117 
118 
119 /* Allocate a "blank" texinfo for a suface. */
glSDL_AllocTexInfo(SDL_Surface * surface)120 glSDL_TexInfo *glSDL_AllocTexInfo(SDL_Surface *surface)
121 {
122 	int handle, i;
123 	glSDL_TexInfo *txi;
124 	if(!surface)
125 		return NULL;
126 
127 	if(!texinfotab)
128 	{
129 		texinfotab = (glSDL_TexInfo**)calloc(MAX_TEXINFOS, sizeof(glSDL_TexInfo *));
130 		if(!texinfotab)
131 			return NULL;
132 	}
133 
134 	txi = glSDL_GetTexInfo(surface);
135 	if(txi)
136 		return txi;		/* There already is one! --> */
137 
138 	/* Find a free handle... */
139 	handle = -1;
140 	for(i = 0; i < MAX_TEXINFOS; ++i)
141 		if(NULL == texinfotab[i])
142 		{
143 			handle = i;
144 			break;
145 		}
146 
147 	if(handle < 0)
148 	{
149 		DBG(fprintf(stderr, "glSDL: Out of handles!\n"));
150 		return NULL;
151 	}
152 
153 	/* ...and hook a new texinfo struct up to it. */
154 	texinfotab[handle] = (glSDL_TexInfo*)calloc(1, sizeof(glSDL_TexInfo));
155 	if(!texinfotab[handle])
156 		return NULL;
157 
158 	/* Connect the surface to the new TexInfo. */
159 	surface->unused1 = (Uint32)handle + 1;
160 
161 	DBG2(fprintf(stderr, "glSDL: Allocated TexInfo %d.\n", handle));
162 
163 	return texinfotab[handle];
164 }
165 
166 
_FreeTexInfo(Uint32 handle)167 static void _FreeTexInfo(Uint32 handle)
168 {
169 	if(handle >= MAX_TEXINFOS)
170 		return;
171 	if(!texinfotab[handle])
172 		return;
173 
174 	_UnloadTexture(texinfotab[handle]);
175 	texinfotab[handle]->textures = 0;
176 	free(texinfotab[handle]->texture);
177 	texinfotab[handle]->texture = NULL;
178 	free(texinfotab[handle]);
179 	texinfotab[handle] = NULL;
180 	DBG2(fprintf(stderr, "glSDL: Freed TexInfo %d.\n", handle));
181 }
182 
183 
184 /* Detach and free the texinfo of a surface. */
glSDL_FreeTexInfo(SDL_Surface * surface)185 void glSDL_FreeTexInfo(SDL_Surface *surface)
186 {
187 	if(!glSDL_GetTexInfo(surface))
188 		return;
189 
190 	_FreeTexInfo(surface->unused1 - 1);
191 	GLSDL_FIX_SURFACE(surface);
192 }
193 
194 
195 /*
196  * Calculate chopping/tiling of a surface to
197  * fit it into the smallest possible OpenGL
198  * texture.
199  */
_CalcChop(glSDL_TexInfo * txi)200 static int _CalcChop(glSDL_TexInfo *txi)
201 {
202 	int rows, vw, vh;
203 	int vertical = 0;
204 	int texsize;
205 	int lastw, lasth, minsize;
206 
207 	vw = txi->virt.w;
208 	vh = txi->virt.h;
209 
210 	DBG3(fprintf(stderr, "w=%d, h=%d ", vw, vh));
211 	if(vh > vw)
212 	{
213 		int t = vw;
214 		vw = vh;
215 		vh = t;
216 		vertical = 1;
217 		DBG3(fprintf(stderr, "(vertical) \t"));
218 	}
219 
220 	/*
221 	 * Check whether this is a "huge" surface - at least one dimension
222 	 * must be <= than the maximum texture size, or we'll have to chop
223 	 * in both directions.
224 	 */
225 	if(vh > maxtexsize)
226 	{
227 		fprintf(stderr, "glSDL: \"Huge\" surfaces not yet supported!\n");
228 		return 0;
229 	}
230 
231 	/* Calculate minimum size */
232 	rows = 1;
233 	lastw = vw;
234 	lasth = vh;
235 	minsize = lastw > lasth ? lastw : lasth;
236 	while(1)
237 	{
238 		int w, h, size;
239 		++rows;
240 		w = vw / rows;
241 		h = rows * vh;
242 		size = w > h ? w : h;
243 		if(size >= minsize)
244 		{
245 			--rows;
246 			break;
247 		}
248 		lastw = w;
249 		lasth = h;
250 		minsize = size;
251 	}
252 	if(minsize > maxtexsize)
253 	{
254 		/* Handle multiple textures for very wide/tall surfaces. */
255 		minsize = maxtexsize;
256 		rows = (vw + minsize-1) / minsize;
257 	}
258 	DBG3(fprintf(stderr, "==> minsize=%d ", minsize));
259 	DBG3(fprintf(stderr, "(rows=%d) \t", rows));
260 
261 	/* Recalculate with nearest higher power-of-2 width. */
262 	for(texsize = 1; texsize < minsize; texsize <<= 1)
263 		;
264 	txi->texsize = texsize;
265 	rows = (vw + texsize-1) / texsize;
266 	DBG3(fprintf(stderr, "==> texsize=%d (rows=%d) \t", texsize, rows));
267 
268 	/* Calculate number of tiles per texture */
269 	txi->tilespertex = txi->texsize / vh;
270 	DBG3(fprintf(stderr, "tilespertex=%d \t", txi->tilespertex));
271 
272 	/* Calculate number of textures needed */
273 	txi->textures = (rows + txi->tilespertex-1) / txi->tilespertex;
274 	txi->texture = (int*)malloc(txi->textures * sizeof(int));
275 	memset(txi->texture, -1, txi->textures * sizeof(int));
276 	DBG3(fprintf(stderr, "textures=%d, ", txi->textures));
277 	if(!txi->texture)
278 	{
279 		fprintf(stderr, "glSDL: INTERNAL ERROR: Failed to allocate"
280 				" texture name table!\n");
281 		return -2;
282 	}
283 
284 	/* Set up tile size. (Only one axis supported here!) */
285 	if(1 == rows)
286 	{
287 		txi->tilemode = GLSDL_TM_SINGLE;
288 		if(vertical)
289 		{
290 			txi->tilew = vh;
291 			txi->tileh = vw;
292 		}
293 		else
294 		{
295 			txi->tilew = vw;
296 			txi->tileh = vh;
297 		}
298 	}
299 	else if(vertical)
300 	{
301 		txi->tilemode = GLSDL_TM_VERTICAL;
302 		txi->tilew = vh;
303 		txi->tileh = texsize;
304 	}
305 	else
306 	{
307 		txi->tilemode = GLSDL_TM_HORIZONTAL;
308 		txi->tilew = texsize;
309 		txi->tileh = vh;
310 	}
311 
312 	DBG3(fprintf(stderr, "tilew=%d, tileh=%d\n", txi->tilew, txi->tileh));
313 	return 0;
314 }
315 
316 
317 /*
318 TODO:
319 	* Keep a list or tree of free texture space rectangles.
320 
321 	* The space manager should actively try to merge
322 	  rectangles from the same texture.
323 
324 static int _AllocTexSpace(glSDL_TexInfo *txi)
325 {
326 	Make sure that _CalcChop() has been called.
327 
328 	Try to find the smallest free area (one or more
329 	tiles) in a single texture that would fit all
330 	tiles of this surface.
331 
332 	if that fails
333 	{
334 		Allocate a new texture of suitable size.
335 		if that fails
336 		{
337 			Try to allocate space for each tile separately.
338 			if that fails
339 				return error -->
340 		}
341 	}
342 
343 	Allocate the areas and link them to the texinfo.
344 	return success -->
345 }
346 */
347 
348 
349 /* Add a glSDL_TexInfo struct to an SDL_Surface */
glSDL_AddTexInfo(SDL_Surface * surface)350 static int glSDL_AddTexInfo(SDL_Surface *surface)
351 {
352 	glSDL_TexInfo *txi;
353 
354 	if(!surface)
355 		return -1;
356 	if(IS_GLSDL_SURFACE(surface))
357 		return 0;	/* Do nothing */
358 
359 	glSDL_AllocTexInfo(surface);
360 	txi = glSDL_GetTexInfo(surface);
361 	if(!txi)
362 		return -2;	/* Oops! Didn't get a texinfo... --> */
363 
364 	txi->virt.w = txi->lw = surface->w;
365 	txi->virt.h = txi->lh = surface->h;
366 
367 	if(_CalcChop(txi) < 0)
368 		return -3;
369 
370 	SDL_SetClipRect(surface, &txi->virt);
371 
372 	return 0;
373 }
374 
375 
376 /* Create a surface of the prefered OpenGL RGB texture format */
_CreateRGBSurface(int w,int h)377 static SDL_Surface *_CreateRGBSurface(int w, int h)
378 {
379 	SDL_Surface *s;
380 	Uint32 rmask, gmask, bmask;
381 	int bits = 24;
382 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
383 	rmask = 0x00ff0000;
384 	gmask = 0x0000ff00;
385 	bmask = 0x000000ff;
386 #else
387 	rmask = 0x000000ff;
388 	gmask = 0x0000ff00;
389 	bmask = 0x00ff0000;
390 #endif
391 	s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
392 			bits, rmask, gmask, bmask, 0);
393 	if(s)
394 		GLSDL_FIX_SURFACE(s);
395 
396 	glSDL_AddTexInfo(s);
397 	return s;
398 }
399 
400 
401 /* Create a surface of the prefered OpenGL RGBA texture format */
_CreateRGBASurface(int w,int h)402 static SDL_Surface *_CreateRGBASurface(int w, int h)
403 {
404 	SDL_Surface *s;
405 	Uint32 rmask, gmask, bmask, amask;
406 	int bits = 32;
407 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
408 	rmask = 0xff000000;
409 	gmask = 0x00ff0000;
410 	bmask = 0x0000ff00;
411 	amask = 0x000000ff;
412 #else
413 	rmask = 0x000000ff;
414 	gmask = 0x0000ff00;
415 	bmask = 0x00ff0000;
416 	amask = 0xff000000;
417 #endif
418 	s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
419 			bits, rmask, gmask, bmask, amask);
420 	if(s)
421 		GLSDL_FIX_SURFACE(s);
422 
423 	glSDL_AddTexInfo(s);
424 	return s;
425 }
426 
427 
_init_formats(void)428 static void _init_formats(void)
429 {
430 	SDL_Surface *s = _CreateRGBSurface(1, 1);
431 	if(!s)
432 		return;
433 	_RGBfmt = *(s->format);
434 	glSDL_FreeSurface(s);
435 
436 	s = _CreateRGBASurface(1, 1);
437 	if(!s)
438 		return;
439 	_RGBAfmt = *(s->format);
440 	glSDL_FreeSurface(s);
441 }
442 
443 
_FormatIsOk(SDL_Surface * surface)444 static int _FormatIsOk(SDL_Surface *surface)
445 {
446 	SDL_PixelFormat *pf;
447 	if(!surface)
448 		return 1;	/* Well, there ain't much we can do anyway... */
449 
450 	pf = surface->format;
451 
452 	/* Colorkeying requires an alpha channel! */
453 	if(surface->flags & SDL_SRCCOLORKEY)
454 		if(!pf->Amask)
455 			return 0;
456 
457 	/* We need pitch == (width * BytesPerPixel) for glTex[Sub]Image2D() */
458 	if(surface->pitch != (surface->w * pf->BytesPerPixel))
459 		return 0;
460 
461 	if(pf->Amask)
462 	{
463 		if(pf->BytesPerPixel != _RGBAfmt.BytesPerPixel)
464 			return 0;
465 		if(pf->Rmask != _RGBAfmt.Rmask)
466 			return 0;
467 		if(pf->Gmask != _RGBAfmt.Gmask)
468 			return 0;
469 		if(pf->Bmask != _RGBAfmt.Bmask)
470 			return 0;
471 		if(pf->Amask != _RGBAfmt.Amask)
472 			return 0;
473 	}
474 	else
475 	{
476 		if(pf->BytesPerPixel != _RGBfmt.BytesPerPixel)
477 			return 0;
478 		if(pf->Rmask != _RGBfmt.Rmask)
479 			return 0;
480 		if(pf->Gmask != _RGBfmt.Gmask)
481 			return 0;
482 		if(pf->Bmask != _RGBfmt.Bmask)
483 			return 0;
484 	}
485 	return 1;
486 }
487 
488 
489 
_key2alpha(SDL_Surface * surface)490 static void _key2alpha(SDL_Surface *surface)
491 {
492 	int x, y;
493 #ifdef CKSTATS
494 	int transp = 0;
495 #endif
496 	Uint32 ckey = surface->format->colorkey;
497 	if(SDL_LockSurface(surface) < 0)
498 		return;
499 
500 	for(y = 0; y < surface->h; ++y)
501 	{
502 		Uint32 *px = (Uint32 *)((char *)surface->pixels + y*surface->pitch);
503 		for(x = 0; x < surface->w; ++x)
504 			if(px[x] == ckey)
505 			{
506 				px[x] = 0;
507 #ifdef CKSTATS
508 				++transp;
509 #endif
510 			}
511 	}
512 #ifdef CKSTATS
513 	printf("glSDL: key2alpha(); %dx%d surface, %d opaque pixels.\n",
514 			surface->w, surface->h,
515 			surface->w * surface->h - transp);
516 #endif
517 	SDL_UnlockSurface(surface);
518 }
519 
520 
521 
522 /*----------------------------------------------------------
523 	SDL style API
524 ----------------------------------------------------------*/
525 
_KillAllTextures(void)526 static void _KillAllTextures(void)
527 {
528 	if(texinfotab)
529 	{
530 		unsigned i;
531 #ifdef LEAK_TRACKING
532 		int leaked = 0;
533 		for(i = 0; i < MAX_TEXINFOS; ++i)
534 			if(texinfotab[i])
535 			{
536 				++leaked;
537 				fprintf(stderr, "glSDL: Leaked TexInfo"
538 						" %d! (%dx%d)\n",
539 						i,
540 						texinfotab[i]->virt.w,
541 						texinfotab[i]->virt.h
542 						);
543 			}
544 		if(leaked)
545 			fprintf(stderr, "glSDL: Leaked %d TexInfos!\n", leaked);
546 #endif
547 		for(i = 0; i < MAX_TEXINFOS; ++i)
548 			_FreeTexInfo(i);
549 		free(texinfotab);
550 		texinfotab = NULL;
551 	}
552 }
553 
glSDL_Quit(void)554 void glSDL_Quit(void)
555 {
556 	if(SDL_WasInit(SDL_INIT_VIDEO))
557 	{
558 		SDL_Surface *screen = SDL_GetVideoSurface();
559 		glSDL_FreeTexInfo(screen);
560 		SDL_QuitSubSystem(SDL_INIT_VIDEO);
561 		if(fake_screen)
562 		{
563 			glSDL_FreeTexInfo(fake_screen);
564 			free(fake_screen);
565 			fake_screen = NULL;
566 		}
567 	}
568 #ifndef LEAK_TRACKING
569 	_KillAllTextures();
570 #endif
571 }
572 
573 
_glSDL_FullQuit(void)574 void _glSDL_FullQuit(void)
575 {
576 #ifdef LEAK_TRACKING
577 	_KillAllTextures();
578 #endif
579 	glSDL_Quit();
580 	SDL_Quit();
581 }
582 
583 
glSDL_QuitSubSystem(Uint32 flags)584 void glSDL_QuitSubSystem(Uint32 flags)
585 {
586 	if(flags & SDL_INIT_VIDEO)
587 		glSDL_Quit();
588 	SDL_QuitSubSystem(flags);
589 }
590 
591 
glSDL_SetVideoMode(int width,int height,int bpp,Uint32 flags)592 SDL_Surface *glSDL_SetVideoMode(int width, int height, int bpp, Uint32 flags)
593 {
594 	SDL_Surface *screen;
595 	GLint gl_doublebuf;
596 	if(!(flags & SDL_GLSDL))
597 	{
598 		screen = SDL_SetVideoMode(width, height, bpp, flags);
599 		if(screen)
600 			GLSDL_FIX_SURFACE(screen);
601 		return screen;
602 	}
603 
604 	if((SDL_Linked_Version()->major <= 1) &&
605 			(SDL_Linked_Version()->minor <= 2) &&
606 			(SDL_Linked_Version()->patch < 5))
607 		fprintf(stderr, "glSDL WARNING: Using SDL version"
608 				" 1.2.5 or later is strongly"
609 				" recommended!\n");
610 
611 /*
612  * FIXME: Here's the place to insert proper handling of this call being
613  *        used for resizing the window... For now, just make sure we
614  *        don't end up with invalid texinfos and stuff no matter what.
615  */
616 	_KillAllTextures();
617 
618 	/* Remove flag to avoid confusion inside SDL - just in case! */
619 	flags &= ~SDL_GLSDL;
620 
621 	flags |= SDL_OPENGL;
622 	if(bpp == 15)
623 	{
624 		SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
625 		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
626 		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
627 	}
628 	else if(bpp == 16)
629 	{
630 		SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
631 		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
632 		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
633 	}
634 	else if(bpp >= 24)
635 	{
636 		SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
637 		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
638 		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
639 	}
640 	gl_doublebuf = flags & SDL_DOUBLEBUF;
641 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, gl_doublebuf);
642 
643 	DBG(printf("desired = %d x %d\n", width, height);)
644 	scale = 1;
645 	while((width*scale < 640) && (height*scale < 480))
646 		++scale;
647 	DBG(printf("real = %d x %d\n", width*scale, height*scale);)
648 
649 	screen = SDL_SetVideoMode(width*scale, height*scale, bpp, flags);
650 	if(!screen)
651 		return NULL;
652 
653 	GLSDL_FIX_SURFACE(screen);
654 
655 #ifdef	FAKE_MAXTEXSIZE
656 	maxtexsize = FAKE_MAXTEXSIZE;
657 #else
658 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize);
659 #endif
660 	DBG(fprintf(stderr, "glSDL: Max texture size: %d\n", maxtexsize));
661 
662 	_init_formats();
663 
664 	if(glSDL_AddTexInfo(screen) < 0)
665 	{
666 		DBG(fprintf(stderr, "glSDL: Failed to add info to screen surface!\n"));
667 		SDL_QuitSubSystem(SDL_INIT_VIDEO);
668 		return NULL;
669 	}
670 
671 	glSDL_SetLogicSize(screen, width*scale, height*scale);
672 
673 	/*
674 	 * Create a software shadow buffer of the requested size.
675 	 * This is used for blit-from-screen and simulation of
676 	 * direct software rendering. (Dog slow crap. It's only
677 	 * legitimate use is probably screen shots.)
678 	 */
679 	fake_screen = _CreateRGBSurface(screen->w / scale,
680 			screen->h / scale);
681 	return fake_screen;
682 }
683 
684 
glSDL_GetVideoSurface(void)685 SDL_Surface *glSDL_GetVideoSurface(void)
686 {
687 	if(fake_screen)
688 		return fake_screen;
689 	else
690 		return SDL_GetVideoSurface();
691 }
692 
693 
glSDL_UpdateRects(SDL_Surface * screen,int numrects,SDL_Rect * rects)694 void glSDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects)
695 {
696 	if(IS_GLSDL_SURFACE(screen))
697 	{
698 /*		if(screen_was_locked)
699 		{
700 			int i;
701 			for(i = 0; i < numrects; ++i)
702 				_glSDL_BlitGL(fake_screen, rects + i,
703 						vs, rects + i);
704 		}
705 		else
706 */
707 			glSDL_Flip(screen);
708 	}
709 	else
710 		SDL_UpdateRects(screen, numrects, rects);
711 	screen_was_locked = 0;
712 }
713 
714 
glSDL_UpdateRect(SDL_Surface * screen,Sint32 x,Sint32 y,Uint32 w,Uint32 h)715 void glSDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h)
716 {
717 	SDL_Rect r;
718 	r.x = x;
719 	r.y = y;
720 	r.w = w;
721 	r.h = h;
722 	glSDL_UpdateRects(screen, 1, &r);
723 }
724 
725 
glSDL_Flip(SDL_Surface * screen)726 int glSDL_Flip(SDL_Surface *screen)
727 {
728 	if(!IS_GLSDL_SURFACE(screen))
729 		return SDL_Flip(screen);
730 /*
731 TODO:	Perform all rendering here, after globally reordering/optimizing
732 	all non-overlapping operations for the frame. (Reduce texture
733 	changes, blend mode changes etc...)
734 
735 Disadvantage:
736 	Won't mix well with user OpenGL calls - but then again, glSDL
737 	isn't meant for OpenGL aware applications in the first place!
738 */
739 	/*
740 	 * Some XFree86 DRI drivers won't sync *at all*
741 	 * without glFinish()! You may end up with commands
742 	 * for several frames buffered up before any actual
743 	 * rendering is done - and then, your program will
744 	 * stall until most of the rendering is completed.
745 	 *
746 	 * (Of course, this wouldn't be much of an issue
747 	 * if the drivers did retrace sync'ed flips, but as
748 	 * most of the drivers don't, there's no way ever an
749 	 * application is going to get smooth animation
750 	 * without this kludge.)
751 	 *
752 	 * Update: That bl**dy *DRIVER* should be fixed!
753 	 *         We don't need this performance killing
754 	 *         kludge. (I know for sure that my current
755 	 *         driver doesn't have this problem.)
756 	 */
757 #ifdef STUPID_GL_WORKAROUND
758 	glFlush();	/* Just in case. *heh* */
759 	SDL_GL_SwapBuffers();
760 	glFinish();	/* And here we kill parallel execution... :-( */
761 #else
762 	SDL_GL_SwapBuffers();
763 #endif
764 	return 0;
765 }
766 
767 
glSDL_FreeSurface(SDL_Surface * surface)768 void glSDL_FreeSurface(SDL_Surface *surface)
769 {
770 	if(!surface)
771 		return;
772 	glSDL_FreeTexInfo(surface);
773 	SDL_FreeSurface(surface);
774 }
775 
776 
glSDL_LockSurface(SDL_Surface * surface)777 int glSDL_LockSurface(SDL_Surface *surface)
778 {
779 	if(!surface)
780 		return 0;
781 
782 	if(IS_GLSDL_SURFACE(surface))
783 	{
784 		if((surface == fake_screen) ||
785 				(SDL_GetVideoSurface() == surface))
786 		{
787 			if(scale > 1)
788 				return -1;
789 
790 			glSDL_Invalidate(fake_screen, NULL);
791 
792 			glPixelStorei(GL_UNPACK_ROW_LENGTH,
793 					fake_screen->pitch /
794 					fake_screen->format->BytesPerPixel);
795 
796 			glReadPixels(0, 0, fake_screen->w, fake_screen->h,
797 					GL_RGB, GL_UNSIGNED_BYTE,
798 					fake_screen->pixels);
799 			return 0;
800 		}
801 		else
802 		{
803 			glSDL_Invalidate(surface, NULL);
804 			return SDL_LockSurface(surface);
805 		}
806 	}
807 	else
808 		return SDL_LockSurface(surface);
809 }
810 
811 
glSDL_UnlockSurface(SDL_Surface * surface)812 void glSDL_UnlockSurface(SDL_Surface *surface)
813 {
814 	if(!surface)
815 		return;
816 
817 	if(IS_GLSDL_SURFACE(surface))
818 	{
819 		glSDL_UploadSurface(surface);
820 		if((surface == fake_screen) ||
821 				(SDL_GetVideoSurface() == surface))
822 			_glSDL_BlitGL(fake_screen, NULL,
823 					SDL_GetVideoSurface(), NULL);
824 	}
825 	else
826 		SDL_UnlockSurface(surface);
827 }
828 
829 
glSDL_SetColorKey(SDL_Surface * surface,Uint32 flag,Uint32 key)830 int glSDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key)
831 {
832 	int res = SDL_SetColorKey(surface, flag, key);
833 	if(res < 0)
834 		return res;
835 	/*
836 	 * If an application does this *after* SDL_DisplayFormat,
837 	 * we're basically screwed, unless we want to do an
838 	 * in-place surface conversion hack here.
839 	 *
840 	 * What we do is just kill the glSDL texinfo... No big
841 	 * deal in most cases, as glSDL only converts once anyway,
842 	 * *unless* you keep modifying the surface.
843 	 */
844 	if(IS_GLSDL_SURFACE(surface))
845 		glSDL_FreeTexInfo(surface);
846 	return res;
847 }
848 
849 
glSDL_SetAlpha(SDL_Surface * surface,Uint32 flag,Uint8 alpha)850 int glSDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha)
851 {
852 	/*
853 	 * This is just parameters to OpenGL, so the actual
854 	 * "work" is done in glSDL_BlitSurface().
855 	 */
856 	return SDL_SetAlpha(surface, flag, alpha);
857 }
858 
859 
glSDL_SetClipRect(SDL_Surface * surface,SDL_Rect * rect)860 SDL_bool glSDL_SetClipRect(SDL_Surface *surface, SDL_Rect *rect)
861 {
862 	SDL_bool res;
863 	SDL_Surface *screen;
864 	SDL_Rect fsr;
865 
866 	if(!surface)
867 		return SDL_FALSE;
868 
869 	screen = SDL_GetVideoSurface();
870 
871 	res = SDL_SetClipRect(surface, rect);
872 	if(!res)
873 		return SDL_FALSE;
874 
875 	if(!rect)
876 	{
877 		fsr.x = 0;
878 		fsr.y = 0;
879 		fsr.w = screen->w;
880 		fsr.h = screen->h;
881 		rect = &fsr;
882 	}
883 
884 	if(surface == fake_screen)
885 	{
886 		SDL_Rect r;
887 		r.x = rect->x * scale;
888 		r.y = rect->y * scale;
889 		r.w = rect->w * scale;
890 		r.h = rect->h * scale;
891 		surface = screen;
892 		SDL_SetClipRect(surface, rect);
893 	}
894 
895 	if( (screen == surface) &&
896 			IS_GLSDL_SURFACE(surface) )
897 	{
898 		float xscale, yscale;
899 		glSDL_TexInfo *txi = glSDL_GetTexInfo(surface);
900 		rect = &surface->clip_rect;
901 		glViewport(	rect->x * scale,
902 				screen->h - (rect->y + rect->h) * scale,
903 				rect->w * scale,
904 				rect->h * scale);
905 		/*
906 		 * Note that this projection is upside down in
907 		 * relation to the OpenGL coordinate system.
908 		 */
909 		glMatrixMode(GL_PROJECTION);
910 		glLoadIdentity();
911 		xscale = (float)txi->lw / (float)surface->w;
912 		yscale = (float)txi->lh / (float)surface->h;
913 		glOrtho(	xscale*(float)rect->x,
914 				xscale*(float)(rect->w+rect->x),
915 				yscale*(float)(rect->h+rect->y),
916 				yscale*(float)rect->y,
917 				-1.0, 1.0);
918 		return SDL_TRUE;
919 	}
920 	return res;
921 }
922 
923 
924 static struct
925 {
926 	int	do_blend;
927 	int	do_texture;
928 	GLint	texture;
929 	GLenum	sfactor, dfactor;
930 } glstate;
931 
gl_reset(void)932 static void gl_reset(void)
933 {
934 	glstate.do_blend = -1;
935 	glstate.do_blend = -1;
936 	glstate.texture = -1;
937 	glstate.sfactor = 0xffffffff;
938 	glstate.dfactor = 0xffffffff;
939 }
940 
gl_do_blend(int on)941 static __inline__ void gl_do_blend(int on)
942 {
943 	if(glstate.do_blend == on)
944 		return;
945 
946 	if(on)
947 		glEnable(GL_BLEND);
948 	else
949 		glDisable(GL_BLEND);
950 	glstate.do_blend = on;
951 }
952 
gl_do_texture(int on)953 static __inline__ void gl_do_texture(int on)
954 {
955 	if(glstate.do_texture == on)
956 		return;
957 
958 	if(on)
959 		glEnable(GL_TEXTURE_2D);
960 	else
961 		glDisable(GL_TEXTURE_2D);
962 	glstate.do_texture = on;
963 }
964 
gl_blendfunc(GLenum sfactor,GLenum dfactor)965 static __inline__ void gl_blendfunc(GLenum sfactor, GLenum dfactor)
966 {
967 	if((sfactor == glstate.sfactor) && (dfactor == glstate.dfactor))
968 		return;
969 
970 	glBlendFunc(sfactor, dfactor);
971 
972 	glstate.sfactor = sfactor;
973 	glstate.dfactor = dfactor;
974 }
975 
gl_texture(GLuint tx)976 static __inline__ void gl_texture(GLuint tx)
977 {
978 	if(tx == glstate.texture)
979 		return;
980 
981 	glBindTexture(GL_TEXTURE_2D, tx);
982 	glstate.texture = tx;
983 }
984 
985 
_glSDL_BlitFromGL(SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)986 static int _glSDL_BlitFromGL(SDL_Rect *srcrect,
987 		SDL_Surface *dst, SDL_Rect *dstrect)
988 {
989 	int i, sy0, dy0;
990 	SDL_Rect sr, dr;
991 
992 	if(scale > 1)
993 		return -1;
994 /*
995 FIXME: Some clipping, perhaps...? :-)
996 */
997 	/* In case the destination has an OpenGL texture... */
998 	glSDL_Invalidate(dst, dstrect);
999 
1000 	/* Abuse the fake screen buffer a little. */
1001 	glPixelStorei(GL_UNPACK_ROW_LENGTH, fake_screen->pitch /
1002 			fake_screen->format->BytesPerPixel);
1003 	if(srcrect)
1004 		glReadPixels(srcrect->x, srcrect->y, srcrect->w, srcrect->h,
1005 				GL_RGB, GL_UNSIGNED_BYTE, fake_screen->pixels);
1006 	else
1007 		glReadPixels(0, 0, fake_screen->w, fake_screen->h,
1008 				GL_RGB, GL_UNSIGNED_BYTE, fake_screen->pixels);
1009 
1010 	/* Blit to the actual target! (Vert. flip... Uuurgh!) */
1011 	if(srcrect)
1012 		sr = *srcrect;
1013 	else
1014 	{
1015 		sr.x = sr.y = 0;
1016 		sr.w = dst->w;
1017 	}
1018 
1019 	if(dstrect)
1020 		dr = *dstrect;
1021 	else
1022 		dr.x = dr.y = 0;
1023 
1024 	i = srcrect->h;
1025 	sy0 = srcrect->y;
1026 	dy0 = dstrect->y + dstrect->h - 1;
1027 	while(i--)
1028 	{
1029 		sr.y = sy0 + i;
1030 		dr.y = dy0 - i;
1031 		sr.h = 1;
1032 		if(SDL_BlitSurface(fake_screen, &sr, dst, &dr) < 0)
1033 			return -1;
1034 	}
1035 	return 0;
1036 }
1037 
1038 
_BlitGL_single(glSDL_TexInfo * txi,float x1,float y1,float x2,float y2,int dx1,float dy1,float dx2,float dy2,unsigned char alpha,float texscale)1039 static __inline__ void _BlitGL_single(glSDL_TexInfo *txi,
1040 		float x1, float y1, float x2, float y2,
1041 		int dx1, float dy1, float dx2, float dy2,
1042 		unsigned char alpha, float texscale)
1043 {
1044 	/* Select texture */
1045 	if(!txi->textures)
1046 		return;
1047 	if(-1 == txi->texture[0])
1048 		return;
1049 	gl_texture(txi->texture[0]);
1050 
1051 	glBegin(GL_QUADS);
1052 	glColor4ub(255, 255, 255, alpha);
1053 	glTexCoord2f(x1, y1);
1054 	glVertex2i(dx1, dy1);
1055 	glTexCoord2f(x2, y1);
1056 	glVertex2i(dx2, dy1);
1057 	glTexCoord2f(x2, y2);
1058 	glVertex2i(dx2, dy2);
1059 	glTexCoord2f(x1, y2);
1060 	glVertex2i(dx1, dy2);
1061 	glEnd();
1062 }
1063 
_BlitGL_htile(glSDL_TexInfo * txi,float x1,float y1,float x2,float y2,int dx1,float dy1,float dx2,float dy2,unsigned char alpha,float texscale)1064 static void _BlitGL_htile(glSDL_TexInfo *txi,
1065 		float x1, float y1, float x2, float y2,
1066 		int dx1, float dy1, float dx2, float dy2,
1067 		unsigned char alpha, float texscale)
1068 {
1069 	float tileh = (float)txi->tileh * texscale;
1070 	float tile = floor(x1);	/* / 1.0 */
1071 	float texsize = (float)txi->texsize;
1072 	int tex = (int)tile / txi->tilespertex;
1073 	float yo = ((int)tile % txi->tilespertex) * tileh;
1074 
1075 	/* Select texture */
1076 	if(tex >= txi->textures)
1077 		return;
1078 	if(-1 == txi->texture[tex])
1079 		return;
1080 	gl_texture(txi->texture[tex]);
1081 
1082 	glBegin(GL_QUADS);
1083 	while(1)
1084 	{
1085 		int thisdx1, thisdx2;
1086 		float thisx1 = x1 - tile;
1087 		float thisx2 = x2 - tile;
1088 
1089 		/* Stop condition */
1090 		if(tile >= x2)
1091 			break;
1092 
1093 		/* Maybe select next texture? */
1094 		if(yo + tileh > 1.0)
1095 		{
1096 			++tex;
1097 			glEnd();
1098 			if(tex >= txi->textures)
1099 				return;
1100 			if(-1 == txi->texture[tex])
1101 				return;
1102 			gl_texture(txi->texture[tex]);
1103 			yo = 0.0;
1104 			glBegin(GL_QUADS);
1105 		}
1106 
1107 		/* Left clip to current tile */
1108 		if(thisx1 < 0.0)
1109 		{
1110 			thisdx1 = dx1 - (int)(thisx1 * texsize);
1111 			thisx1 = 0.0;
1112 		}
1113 		else
1114 			thisdx1 = dx1;
1115 
1116 		/* Right clip to current tile */
1117 		if(thisx2 > 1.0)
1118 		{
1119 			thisdx2 = dx2 - (int)((thisx2 - 1.0) * texsize);
1120 			thisx2 = 1.0;
1121 		}
1122 		else
1123 			thisdx2 = dx2;
1124 
1125 		glColor4ub(255, 255, 255, alpha);
1126 		glTexCoord2f(thisx1, yo + y1);
1127 		glVertex2i(thisdx1, dy1);
1128 		glTexCoord2f(thisx2, yo + y1);
1129 		glVertex2i(thisdx2, dy1);
1130 		glTexCoord2f(thisx2, yo + y2);
1131 		glVertex2i(thisdx2, dy2);
1132 		glTexCoord2f(thisx1, yo + y2);
1133 		glVertex2i(thisdx1, dy2);
1134 
1135 		tile += 1.0;
1136 		yo += tileh;
1137 	}
1138 	glEnd();
1139 }
1140 
_BlitGL_vtile(glSDL_TexInfo * txi,float x1,float y1,float x2,float y2,int dx1,float dy1,float dx2,float dy2,unsigned char alpha,float texscale)1141 static void _BlitGL_vtile(glSDL_TexInfo *txi,
1142 		float x1, float y1, float x2, float y2,
1143 		int dx1, float dy1, float dx2, float dy2,
1144 		unsigned char alpha, float texscale)
1145 {
1146 	float tilew = (float)txi->tilew * texscale;
1147 	float tile = floor(y1);
1148 	float texsize = (float)txi->texsize;
1149 	float xo = tile * tilew;
1150 	int tex = ((int)tile * txi->tilew + txi->tilew-1) / txi->texsize;
1151 
1152 	/* Select texture */
1153 	if(tex >= txi->textures)
1154 		return;
1155 	if(-1 == txi->texture[tex])
1156 		return;
1157 	gl_texture(txi->texture[tex]);
1158 
1159 	glBegin(GL_QUADS);
1160 	while(1)
1161 	{
1162 		int newtex;
1163 		int thisdy1, thisdy2;
1164 		float thisy1 = y1 - tile;
1165 		float thisy2 = y2 - tile;
1166 
1167 		/* Stop condition */
1168 		if(tile >= y2)
1169 			break;
1170 
1171 		/* Maybe select next texture? */
1172 		newtex = ((int)tile * txi->tilew + txi->tilew-1) /
1173 				txi->texsize;
1174 		if(newtex != tex)
1175 		{
1176 			tex = newtex;
1177 			glEnd();
1178 			if(tex >= txi->textures)
1179 				return;
1180 			if(-1 == txi->texture[tex])
1181 				return;
1182 			gl_texture(txi->texture[tex]);
1183 			xo = 0.0;
1184 			glBegin(GL_QUADS);
1185 		}
1186 
1187 		/* Left clip to current tile */
1188 		if(thisy1 < 0.0)
1189 		{
1190 			thisdy1 = dy1 - (int)(thisy1 * texsize);
1191 			thisy1 = 0.0;
1192 		}
1193 		else
1194 			thisdy1 = dy1;
1195 
1196 		/* Right clip to current tile */
1197 		if(thisy2 > 1.0)
1198 		{
1199 			thisdy2 = dy2 - (int)((thisy2 - 1.0) * texsize);
1200 			thisy2 = 1.0;
1201 		}
1202 		else
1203 			thisdy2 = dy2;
1204 
1205 		glColor4ub(255, 255, 255, alpha);
1206 		glTexCoord2f(xo + x1, thisy1);
1207 		glVertex2i(dx1, thisdy1);
1208 		glTexCoord2f(xo + x2, thisy1);
1209 		glVertex2i(dx2, thisdy1);
1210 		glTexCoord2f(xo + x2, thisy2);
1211 		glVertex2i(dx2, thisdy2);
1212 		glTexCoord2f(xo + x1, thisy2);
1213 		glVertex2i(dx1, thisdy2);
1214 
1215 		tile += 1.0;
1216 		xo += tilew;
1217 	}
1218 	glEnd();
1219 }
1220 
_glSDL_BlitGL(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)1221 static int _glSDL_BlitGL(SDL_Surface *src, SDL_Rect *srcrect,
1222 			 SDL_Surface *dst, SDL_Rect *dstrect)
1223 {
1224 	glSDL_TexInfo *txi;
1225 	float x1, y1, x2, y2;
1226 	int dx1, dy1, dx2, dy2;
1227 	float texscale;
1228 	unsigned char alpha;
1229 	if(!src || !dst)
1230 		return -1;
1231 
1232 	/* Cull off-screen blits. */
1233 	if(dstrect)
1234 	{
1235 		if(dstrect->x > dst->w)
1236 			return 0;
1237 		if(dstrect->y > dst->h)
1238 			return 0;
1239 		if(srcrect)
1240 		{
1241 			if(dstrect->x + srcrect->w < 0)
1242 				return 0;
1243 			if(dstrect->y + srcrect->h < 0)
1244 				return 0;
1245 		}
1246 		else
1247 		{
1248 			if(dstrect->x + src->w < 0)
1249 				return 0;
1250 			if(dstrect->y + src->h < 0)
1251 				return 0;
1252 		}
1253 	}
1254 
1255 	/* Make sure we have a source with a valid texture */
1256 	glSDL_UploadSurface(src);
1257 	txi = glSDL_GetTexInfo(src);
1258 
1259 	/* Set up blending */
1260 	if(src->flags & (SDL_SRCALPHA | SDL_SRCCOLORKEY))
1261 	{
1262 		gl_blendfunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1263 		gl_do_blend(1);
1264 	}
1265 	else
1266 		gl_do_blend(0);
1267 
1268 	/* Enable texturing */
1269 	gl_do_texture(1);
1270 
1271 	/* Calculate texcoords */
1272 	if(!srcrect)
1273 		srcrect = &txi->virt;
1274 	texscale = 1.0 / (float)txi->texsize;
1275 	x1 = (float)srcrect->x * texscale;
1276 	y1 = (float)srcrect->y * texscale;
1277 	x2 = (float)(srcrect->x+srcrect->w) * texscale;
1278 	y2 = (float)(srcrect->y+srcrect->h) * texscale;
1279 
1280 	/* Calculate screen coords. */
1281 	dx2 = srcrect->w * (float)txi->lw / (float)txi->virt.w;
1282 	dy2 = srcrect->h * (float)txi->lh / (float)txi->virt.h;
1283 	if(dstrect)
1284 	{
1285 		dx1 = dstrect->x;
1286 		dy1 = dstrect->y;
1287 		/*
1288 		 * FIXME: dstrect should be filled in with the *clipped*
1289 		 *        rect for full SDL compatibility. This hack
1290 		 *	  might break some apps...
1291 		 */
1292 		dstrect->w = dx2;
1293 		dstrect->h = dy2;
1294 	}
1295 	else
1296 		dx1 = dy1 = 0;
1297 	dx2 += dx1;
1298 	dy2 += dy1;
1299 
1300 	/*
1301 	 * Note that we actually *prevent* the use of "full surface alpha"
1302 	 * and alpha channel in combination - to stay SDL 2D compatible.
1303 	 */
1304 	if((src->flags & SDL_SRCALPHA) &&
1305 			(!src->format->Amask || (src->flags & SDL_SRCCOLORKEY)))
1306 		alpha = src->format->alpha;
1307 	else
1308 		alpha = 255;
1309 
1310 	/* Render! */
1311 	switch(txi->tilemode)
1312 	{
1313 	  case GLSDL_TM_SINGLE:
1314 		_BlitGL_single(txi, x1, y1, x2, y2,
1315 				dx1, dy1, dx2, dy2,
1316 				alpha, texscale);
1317 		break;
1318 	  case GLSDL_TM_HORIZONTAL:
1319 		_BlitGL_htile(txi, x1, y1, x2, y2,
1320 				dx1, dy1, dx2, dy2,
1321 				alpha, texscale);
1322 		break;
1323 	  case GLSDL_TM_VERTICAL:
1324 		_BlitGL_vtile(txi, x1, y1, x2, y2,
1325 				dx1, dy1, dx2, dy2,
1326 				alpha, texscale);
1327 		break;
1328 	  case GLSDL_TM_HUGE:
1329 		/* TODO */
1330 		break;
1331 	}
1332 
1333 	return 0;
1334 }
1335 
1336 
glSDL_BlitSurface(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)1337 int glSDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect,
1338 		SDL_Surface *dst, SDL_Rect *dstrect)
1339 {
1340 	SDL_Surface *vs;
1341 	if(!src || !dst)
1342 		return -1;
1343 
1344 	/*
1345 	 * Figure out what to do:
1346 	 *      Not using glSDL:        SDL_BlitSurface()
1347 	 *      screen->screen:         _glSDL_BlitFromGL() + _glSDL_BlitGL()
1348 	 *      surface->screen:        _glSDL_BlitGL()
1349 	 *      screen->surface:        _glSDL_BlitFromGL()
1350 	 *      surface->surface:       SDL_BlitSurface()
1351 	 */
1352 	if(!USING_GLSDL)
1353 		return SDL_BlitSurface(src, srcrect, dst, dstrect);
1354 
1355 	vs = SDL_GetVideoSurface();
1356 	if(src == fake_screen)
1357 		src = vs;
1358 	if(dst == fake_screen)
1359 		dst = vs;
1360 	if(src == vs)
1361 	{
1362 		if(dst == vs)
1363 		{
1364 /*
1365 FIXME: Any OpenGL extensions for this...?
1366 */
1367 			_glSDL_BlitFromGL(srcrect, fake_screen, dstrect);
1368 			return _glSDL_BlitGL(fake_screen, srcrect,
1369 					dst, dstrect);
1370 		}
1371 		else
1372 		{
1373 			return _glSDL_BlitFromGL(srcrect, dst, dstrect);
1374 		}
1375 	}
1376 	else
1377 	{
1378 		if(dst == vs)
1379 		{
1380 			return _glSDL_BlitGL(src, srcrect,
1381 					dst, dstrect);
1382 		}
1383 		else
1384 		{
1385 			glSDL_Invalidate(dst, dstrect);
1386 			return SDL_BlitSurface(src, srcrect, dst, dstrect);
1387 		}
1388 	}
1389 }
1390 
1391 
glSDL_FillRect(SDL_Surface * dst,SDL_Rect * dstrect,Uint32 color)1392 int glSDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color)
1393 {
1394 	SDL_Surface *vs = SDL_GetVideoSurface();
1395 	int dx1, dy1, dx2, dy2;
1396 	Uint32 r, g, b;
1397 
1398 	/*
1399 	 * Some ugly reverse conversion for compatibility...
1400 	 * (We must do this before losing the dst pointer,
1401 	 * as the pixel formats of the screen and the
1402 	 * fake_screen may differ!)
1403 	 */
1404 	r = color & dst->format->Rmask;
1405 	r = r >> dst->format->Rshift;
1406 	r = r << dst->format->Rloss;
1407 
1408 	g = color & dst->format->Gmask;
1409 	g = g >> dst->format->Gshift;
1410 	g = g << dst->format->Gloss;
1411 
1412 	b = color & dst->format->Bmask;
1413 	b = b >> dst->format->Bshift;
1414 	b = b << dst->format->Bloss;
1415 
1416 	if(dst == fake_screen)
1417 		dst = vs;
1418 
1419 	if(vs != dst)
1420 		glSDL_Invalidate(dst, dstrect);
1421 
1422 	if((vs != dst) || !USING_GLSDL)
1423 		return SDL_FillRect(dst, dstrect, color);
1424 
1425 	gl_do_texture(0);
1426 	gl_do_blend(0);
1427 
1428 	if(!dstrect)
1429 		dstrect = &dst->clip_rect;
1430 
1431 	dx1 = dstrect->x;
1432 	dy1 = dstrect->y;
1433 	dx2 = dx1 + dstrect->w;
1434 	dy2 = dy1 + dstrect->h;
1435 
1436 	glBegin(GL_QUADS);
1437 	glColor3ub(r, g, b);
1438 	glVertex2i(dx1, dy1);
1439 	glVertex2i(dx2, dy1);
1440 	glVertex2i(dx2, dy2);
1441 	glVertex2i(dx1, dy2);
1442 	glEnd();
1443 
1444 	return 0;
1445 }
1446 
1447 
glSDL_DisplayFormat(SDL_Surface * surface)1448 SDL_Surface *glSDL_DisplayFormat(SDL_Surface *surface)
1449 {
1450 	SDL_Surface *s, *tmp;
1451 	if(USING_GLSDL)
1452 	{
1453 		int use_rgba = (surface->flags & SDL_SRCCOLORKEY) ||
1454 				((surface->flags & SDL_SRCALPHA) &&
1455 				surface->format->Amask);
1456 		if(use_rgba)
1457 			tmp = SDL_ConvertSurface(surface, &_RGBAfmt, SDL_SWSURFACE);
1458 		else
1459 			tmp = SDL_ConvertSurface(surface, &_RGBfmt, SDL_SWSURFACE);
1460 		if(!tmp)
1461 			return NULL;
1462 		GLSDL_FIX_SURFACE(tmp);
1463 		SDL_SetAlpha(tmp, 0, 0);
1464 
1465 		if(surface->flags & SDL_SRCCOLORKEY)
1466 		{
1467 			/*
1468 			 * We drop colorkey data here, but we have to,
1469 			 * or we'll run into trouble when converting,
1470 			 * in particular from indexed color formats.
1471 			 */
1472 			SDL_SetColorKey(tmp, SDL_SRCCOLORKEY,
1473 					surface->format->colorkey);
1474 			_key2alpha(tmp);
1475 		}
1476 		SDL_SetColorKey(tmp, 0, 0);
1477 
1478 		if(use_rgba)
1479 			s = _CreateRGBASurface(surface->w, surface->h);
1480 		else
1481 			s = _CreateRGBSurface(surface->w, surface->h);
1482 		if(!s)
1483 		{
1484 			glSDL_FreeSurface(tmp);
1485 			return NULL;
1486 		}
1487 		SDL_BlitSurface(tmp, NULL, s, NULL);
1488 		glSDL_FreeSurface(tmp);
1489 
1490 		if(surface->flags & SDL_SRCALPHA)
1491 			SDL_SetAlpha(s, SDL_SRCALPHA,
1492 					surface->format->alpha);
1493 		return s;
1494 	}
1495 	else
1496 	{
1497 		s = SDL_DisplayFormat(surface);
1498 		if(s)
1499 			GLSDL_FIX_SURFACE(s);
1500 		return s;
1501 	}
1502 }
1503 
1504 
glSDL_DisplayFormatAlpha(SDL_Surface * surface)1505 SDL_Surface *glSDL_DisplayFormatAlpha(SDL_Surface *surface)
1506 {
1507 	SDL_Surface *s, *tmp;
1508 	if(USING_GLSDL)
1509 	{
1510 		tmp = SDL_ConvertSurface(surface, &_RGBAfmt, SDL_SWSURFACE);
1511 		if(!tmp)
1512 			return NULL;
1513 		GLSDL_FIX_SURFACE(tmp);
1514 
1515 		SDL_SetAlpha(tmp, 0, 0);
1516 		SDL_SetColorKey(tmp, 0, 0);
1517 		s = _CreateRGBASurface(surface->w, surface->h);
1518 		if(!s)
1519 		{
1520 			glSDL_FreeSurface(tmp);
1521 			return NULL;
1522 		}
1523 		SDL_BlitSurface(tmp, NULL, s, NULL);
1524 		glSDL_FreeSurface(tmp);
1525 
1526 		if(surface->flags & SDL_SRCCOLORKEY)
1527 		{
1528 			SDL_SetColorKey(s, SDL_SRCCOLORKEY,
1529 					surface->format->colorkey);
1530 			_key2alpha(s);
1531 		}
1532 		if(surface->flags & SDL_SRCALPHA)
1533 			SDL_SetAlpha(s, SDL_SRCALPHA,
1534 					surface->format->alpha);
1535 		return s;
1536 	}
1537 	else
1538 	{
1539 		s = SDL_DisplayFormatAlpha(surface);
1540 		if(s)
1541 			GLSDL_FIX_SURFACE(s);
1542 		return s;
1543 	}
1544 }
1545 
1546 
glSDL_ConvertSurface(SDL_Surface * src,SDL_PixelFormat * fmt,Uint32 flags)1547 SDL_Surface *glSDL_ConvertSurface
1548 			(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags)
1549 {
1550 	SDL_Surface *s = SDL_ConvertSurface(src, fmt, flags);
1551 	if(s)
1552 		GLSDL_FIX_SURFACE(s);
1553 	return s;
1554 }
1555 
1556 
glSDL_CreateRGBSurface(Uint32 flags,int width,int height,int depth,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)1557 SDL_Surface *glSDL_CreateRGBSurface
1558 			(Uint32 flags, int width, int height, int depth,
1559 			Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
1560 {
1561 	SDL_Surface *s = SDL_CreateRGBSurface(flags, width, height, depth,
1562 			Rmask, Gmask, Bmask, Amask);
1563 	if(s)
1564 		GLSDL_FIX_SURFACE(s);
1565 	return s;
1566 }
1567 
1568 
glSDL_CreateRGBSurfaceFrom(void * pixels,int width,int height,int depth,int pitch,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)1569 SDL_Surface *glSDL_CreateRGBSurfaceFrom(void *pixels,
1570 			int width, int height, int depth, int pitch,
1571 			Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
1572 {
1573 	SDL_Surface *s = SDL_CreateRGBSurfaceFrom(pixels,
1574 			width, height, depth, pitch,
1575 			Rmask, Gmask, Bmask, Amask);
1576 	if(s)
1577 		GLSDL_FIX_SURFACE(s);
1578 	return s;
1579 }
1580 
1581 
glSDL_LoadBMP(const char * file)1582 SDL_Surface *glSDL_LoadBMP(const char *file)
1583 {
1584 	SDL_Surface *s = SDL_LoadBMP(file);
1585 	if(s)
1586 		GLSDL_FIX_SURFACE(s);
1587 	return s;
1588 }
1589 
1590 
glSDL_SaveBMP(SDL_Surface * surface,const char * file)1591 int glSDL_SaveBMP(SDL_Surface *surface, const char *file)
1592 {
1593 	SDL_Rect r;
1594 	SDL_Surface *buf;
1595 	SDL_Surface *screen = SDL_GetVideoSurface();
1596 
1597 	if(!USING_GLSDL)
1598 		return SDL_SaveBMP(surface, file);
1599 
1600 	if((surface != screen) && (surface != fake_screen))
1601 		return SDL_SaveBMP(surface, file);
1602 
1603 	buf = _CreateRGBSurface(fake_screen->w, fake_screen->h);
1604 
1605 	r.x = 0;
1606 	r.y = 0;
1607 	r.w = fake_screen->w;
1608 	r.h = fake_screen->h;
1609 	if(_glSDL_BlitFromGL(&r, buf, &r) < 0)
1610 		return -1;
1611 
1612 	return SDL_SaveBMP(buf, file);
1613 
1614 	glSDL_FreeSurface(buf);
1615 }
1616 
1617 
1618 
1619 
1620 /*----------------------------------------------------------
1621 	glSDL specific API extensions
1622 ----------------------------------------------------------*/
1623 
glSDL_Invalidate(SDL_Surface * surface,SDL_Rect * area)1624 void glSDL_Invalidate(SDL_Surface *surface, SDL_Rect *area)
1625 {
1626 	glSDL_TexInfo *txi;
1627 	if(!surface)
1628 		return;
1629 	txi = glSDL_GetTexInfo(surface);
1630 	if(!txi)
1631 		return;
1632 	if(!area)
1633 	{
1634 		txi->invalid_area.x = 0;
1635 		txi->invalid_area.y = 0;
1636 		txi->invalid_area.w = surface->w;
1637 		txi->invalid_area.h = surface->h;
1638 		return;
1639 	}
1640 	txi->invalid_area = *area;
1641 }
1642 
1643 
glSDL_SetLogicSize(SDL_Surface * surface,int w,int h)1644 void glSDL_SetLogicSize(SDL_Surface *surface, int w, int h)
1645 {
1646 	SDL_Rect r;
1647 	glSDL_TexInfo *txi;
1648 	if(!IS_GLSDL_SURFACE(surface))
1649 		return;
1650 
1651 	txi = glSDL_GetTexInfo(surface);
1652 
1653 	txi->lw = w;
1654 	txi->lh = h;
1655 
1656 	if((SDL_GetVideoSurface() != surface) && (fake_screen != surface))
1657 		return;
1658 
1659 	r.x = r.y = 0;
1660 	r.w = w;
1661 	r.h = h;
1662 	glSDL_SetClipRect(surface, &r);
1663 
1664 	glMatrixMode(GL_MODELVIEW);
1665 	glLoadIdentity();
1666 	glTranslatef(0.0f, 0.0f, 0.0f);
1667 
1668 	glDisable(GL_DEPTH_TEST);
1669 	glDisable(GL_CULL_FACE);
1670 
1671 	gl_reset();
1672 }
1673 
1674 
1675 /* Upload a single texture. */
_UploadTexture(SDL_Surface * datasurf,glSDL_TexInfo * txi,int tex)1676 static int _UploadTexture(SDL_Surface *datasurf, glSDL_TexInfo *txi, int tex)
1677 {
1678 	int bpp = datasurf->format->BytesPerPixel;
1679 
1680 	glGenTextures(1, (unsigned int *)&txi->texture[tex]);
1681 	glBindTexture(GL_TEXTURE_2D, txi->texture[tex]);
1682 	glPixelStorei(GL_UNPACK_ROW_LENGTH, datasurf->pitch /
1683 			datasurf->format->BytesPerPixel);
1684 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1685 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1686 	glTexImage2D(GL_TEXTURE_2D, 0,
1687 			datasurf->format->Amask ? GL_RGBA8 : GL_RGB8,
1688 			txi->texsize, txi->texsize, 0,
1689 			datasurf->format->Amask ? GL_RGBA : GL_RGB,
1690 			GL_UNSIGNED_BYTE, NULL);
1691 	DBG(_print_glerror(1));
1692 
1693 	switch(txi->tilemode)
1694 	{
1695 	  case GLSDL_TM_SINGLE:
1696 	  case GLSDL_TM_HORIZONTAL:
1697 	  {
1698 		/* Image tiled horizontally, or not at all */
1699 		int fromx = txi->tilew * tex * txi->tilespertex;
1700 		int toy = 0;
1701 		while(toy + txi->tileh <= txi->texsize)
1702 		{
1703 			int thistw;
1704 			thistw = datasurf->w - fromx;
1705 			if(thistw > txi->tilew)
1706 				thistw = txi->tilew;
1707 			else if(thistw <= 0)
1708 				break;
1709 			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, toy,
1710 					thistw, txi->tileh,
1711 					datasurf->format->Amask ? GL_RGBA : GL_RGB,
1712 					GL_UNSIGNED_BYTE,
1713 					(char *)datasurf->pixels + bpp * fromx);
1714 			DBG4(_print_glerror(2));
1715 			fromx += txi->tilew;
1716 			toy += txi->tileh;
1717 			glFlush();
1718 		}
1719 		break;
1720 	  }
1721 	  case GLSDL_TM_VERTICAL:
1722 	  {
1723 		/* Image tiled vertically */
1724 		int fromy = txi->tileh * tex * txi->tilespertex;
1725 		int tox = 0;
1726 		while(tox + txi->tilew <= txi->texsize)
1727 		{
1728 			int thisth;
1729 			thisth = datasurf->h - fromy;
1730 			if(thisth > txi->tileh)
1731 				thisth = txi->tileh;
1732 			else if(thisth <= 0)
1733 				break;
1734 			glTexSubImage2D(GL_TEXTURE_2D, 0, tox, 0,
1735 					txi->tilew, thisth,
1736 					datasurf->format->Amask ? GL_RGBA : GL_RGB,
1737 					GL_UNSIGNED_BYTE,
1738 					(char *)datasurf->pixels + datasurf->pitch * fromy);
1739 			DBG4(_print_glerror(3));
1740 			fromy += txi->tileh;
1741 			tox += txi->tilew;
1742 			glFlush();
1743 		}
1744 		break;
1745 	  }
1746 	  case GLSDL_TM_HUGE:
1747 		/* "Huge" image - tiled both ways */
1748 		return -4;
1749 		break;
1750 	}
1751 	return 0;
1752 }
1753 
1754 
glSDL_UploadSurface(SDL_Surface * surface)1755 int glSDL_UploadSurface(SDL_Surface *surface)
1756 {
1757 	SDL_Surface *datasurf = surface;
1758 	glSDL_TexInfo *txi;
1759 	int i;
1760 
1761 	/*
1762 	 * For now, we just assume that *every* texture needs
1763 	 * conversion before uploading.
1764 	 */
1765 
1766 	/* If there's no TexInfo, add one. */
1767 	if(!IS_GLSDL_SURFACE(surface))
1768 		glSDL_AddTexInfo(surface);
1769 
1770 	txi = glSDL_GetTexInfo(surface);
1771 
1772 	/* No partial updates implemented yet... */
1773 	if(txi->invalid_area.w)
1774 		glSDL_UnloadSurface(surface);
1775 	else
1776 	{
1777 		int missing = 0;
1778 		if(txi->textures)
1779 		{
1780 			for(i = 0; i < txi->textures; ++i)
1781 				if(-1 == txi->texture[i])
1782 				{
1783 					missing = 1;
1784 					break;
1785 				}
1786 			if(!missing)
1787 				return 0;	/* They're already there! */
1788 		}
1789 	}
1790 
1791 	if(txi->texsize > maxtexsize)
1792 	{
1793 		fprintf(stderr, "glSDL: INTERNAL ERROR: Too large texture!\n");
1794 		return -1;	/* This surface wasn't tiled properly... */
1795 	}
1796 
1797 	/*
1798 	 * Kludge: Convert if not of preferred RGB or RGBA format.
1799 	 *
1800 	 *	Conversion should only be done when *really* needed.
1801 	 *	That is, it should rarely have to be done with OpenGL
1802 	 *	1.2+.
1803 	 *
1804 	 *	Besides, any surface that's been SDL_DisplayFormat()ed
1805 	 *	should already be in the best known OpenGL format -
1806 	 *	preferably one that makes DMA w/o conversion possible.
1807 	 */
1808 	if(_FormatIsOk(surface))
1809 		datasurf = surface;
1810 	else
1811 	{
1812 		DBG(fprintf(stderr, "glSDL: WARNING: On-the-fly conversion performed!\n"));
1813 		if(surface->format->Amask)
1814 			datasurf = glSDL_DisplayFormatAlpha(surface);
1815 		else
1816 			datasurf = glSDL_DisplayFormat(surface);
1817 		if(!datasurf)
1818 			return -2;
1819 	}
1820 
1821 	for(i = 0; i < txi->textures; ++i)
1822 		if(_UploadTexture(datasurf, txi, i) < 0)
1823 			return -3;
1824 
1825 	if(datasurf != surface)
1826 		glSDL_FreeSurface(datasurf);
1827 	return 0;
1828 }
1829 
1830 
_UnloadTexture(glSDL_TexInfo * txi)1831 static void _UnloadTexture(glSDL_TexInfo *txi)
1832 {
1833 	int i;
1834 	for(i = 0; i < txi->textures; ++i)
1835 		glDeleteTextures(1, (unsigned int *)&txi->texture[i]);
1836 	memset(&txi->invalid_area, 0, sizeof(txi->invalid_area));
1837 }
1838 
1839 
glSDL_UnloadSurface(SDL_Surface * surface)1840 void glSDL_UnloadSurface(SDL_Surface *surface)
1841 {
1842 	if(!IS_GLSDL_SURFACE(surface))
1843 		return;
1844 
1845 	_UnloadTexture(glSDL_GetTexInfo(surface));
1846 }
1847 
1848 
glSDL_IMG_Load(const char * file)1849 SDL_Surface *glSDL_IMG_Load(const char *file)
1850 {
1851 	SDL_Surface *s;
1852 	s = IMG_Load(file);
1853 	if(s)
1854 		GLSDL_FIX_SURFACE(s);
1855 	return s;
1856 }
1857 
1858 #endif /* HAVE_OPENGL */
1859