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