rnd-20060816-3-src
[rocksndiamonds.git] / src / libgame / sdl.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * sdl.c                                                    *
12 ***********************************************************/
13
14 #include "system.h"
15 #include "sound.h"
16 #include "joystick.h"
17 #include "misc.h"
18 #include "setup.h"
19
20
21 #if defined(TARGET_SDL)
22
23 /* ========================================================================= */
24 /* video functions                                                           */
25 /* ========================================================================= */
26
27 /* functions from SGE library */
28 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
29
30 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
31 static int fullscreen_width;
32 static int fullscreen_height;
33 static int fullscreen_xoffset;
34 static int fullscreen_yoffset;
35 static int video_xoffset;
36 static int video_yoffset;
37
38 static void setFullscreenParameters()
39 {
40   struct ScreenModeInfo *fullscreen_mode;
41   int i;
42
43   fullscreen_mode = get_screen_mode_from_string(setup.fullscreen_mode);
44
45   if (fullscreen_mode == NULL)
46     return;
47
48   for (i = 0; video.fullscreen_modes[i].width != -1; i++)
49   {
50     if (fullscreen_mode->width  == video.fullscreen_modes[i].width &&
51         fullscreen_mode->height == video.fullscreen_modes[i].height)
52     {
53       fullscreen_width  = fullscreen_mode->width;
54       fullscreen_height = fullscreen_mode->height;
55
56       fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
57       fullscreen_yoffset = (fullscreen_height - video.height) / 2;
58
59       break;
60     }
61   }
62 }
63
64 static void SDLSetWindowIcon(char *basename)
65 {
66   char *filename = getCustomImageFilename(basename);
67   SDL_Surface *surface;
68
69   if (filename == NULL)
70   {
71     Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
72
73     return;
74   }
75
76   if ((surface = IMG_Load(filename)) == NULL)
77   {
78     Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
79
80     return;
81   }
82
83   /* set transparent color */
84   SDL_SetColorKey(surface, SDL_SRCCOLORKEY,
85                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
86
87   SDL_WM_SetIcon(surface, NULL);
88 }
89
90 void SDLInitVideoDisplay(void)
91 {
92   putenv("SDL_VIDEO_CENTERED=1");
93
94   /* initialize SDL video */
95   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
96     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
97
98   /* set default SDL depth */
99   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
100 }
101
102 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
103                         boolean fullscreen)
104 {
105   static int screen_xy[][2] =
106   {
107     {  640, 480 },
108     {  800, 600 },
109     { 1024, 768 },
110     {   -1,  -1 }
111   };
112   SDL_Rect **modes;
113   int i, j;
114
115   /* default: normal game window size */
116   fullscreen_width = video.width;
117   fullscreen_height = video.height;
118   fullscreen_xoffset = 0;
119   fullscreen_yoffset = 0;
120
121   for (i = 0; screen_xy[i][0] != -1; i++)
122   {
123     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
124     {
125       fullscreen_width  = screen_xy[i][0];
126       fullscreen_height = screen_xy[i][1];
127
128       break;
129     }
130   }
131
132   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
133   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
134
135   /* get available hardware supported fullscreen modes */
136   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
137
138   if (modes == NULL)
139   {
140     /* no screen modes available => no fullscreen mode support */
141     video.fullscreen_available = FALSE;
142   }
143   else if (modes == (SDL_Rect **)-1)
144   {
145     /* fullscreen resolution is not restricted -- all resolutions available */
146     video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
147
148     /* use native video buffer size for fullscreen mode */
149     video.fullscreen_modes[0].width  = video.width;
150     video.fullscreen_modes[0].height = video.height;
151
152     video.fullscreen_modes[1].width  = -1;
153     video.fullscreen_modes[1].height = -1;
154   }
155   else
156   {
157     /* in this case, a certain number of screen modes is available */
158     int num_modes = 0;
159
160     for(i = 0; modes[i] != NULL; i++)
161     {
162       boolean found_mode = FALSE;
163
164       /* screen mode is smaller than video buffer size -- skip it */
165       if (modes[i]->w < video.width || modes[i]->h < video.height)
166         continue;
167
168       if (video.fullscreen_modes != NULL)
169         for (j = 0; video.fullscreen_modes[j].width != -1; j++)
170           if (modes[i]->w == video.fullscreen_modes[j].width &&
171               modes[i]->h == video.fullscreen_modes[j].height)
172             found_mode = TRUE;
173
174       if (found_mode)           /* screen mode already stored -- skip it */
175         continue;
176
177       /* new mode found; add it to list of available fullscreen modes */
178
179       num_modes++;
180
181       video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
182                                                (num_modes + 1) *
183                                                sizeof(struct ScreenModeInfo));
184
185       video.fullscreen_modes[num_modes - 1].width  = modes[i]->w;
186       video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
187
188       video.fullscreen_modes[num_modes].width  = -1;
189       video.fullscreen_modes[num_modes].height = -1;
190     }
191
192     if (num_modes == 0)
193     {
194       /* no appropriate screen modes available => no fullscreen mode support */
195       video.fullscreen_available = FALSE;
196     }
197   }
198
199   /* set window icon */
200   SDLSetWindowIcon(program.sdl_icon_filename);
201
202   /* open SDL video output device (window or fullscreen mode) */
203   if (!SDLSetVideoMode(backbuffer, fullscreen))
204     Error(ERR_EXIT, "setting video mode failed");
205
206   /* set window and icon title */
207   SDL_WM_SetCaption(program.window_title, program.window_title);
208
209   /* SDL cannot directly draw to the visible video framebuffer like X11,
210      but always uses a backbuffer, which is then blitted to the visible
211      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
212      visible video framebuffer with 'SDL_Flip', if the hardware supports
213      this). Therefore do not use an additional backbuffer for drawing, but
214      use a symbolic buffer (distinguishable from the SDL backbuffer) called
215      'window', which indicates that the SDL backbuffer should be updated to
216      the visible video framebuffer when attempting to blit to it.
217
218      For convenience, it seems to be a good idea to create this symbolic
219      buffer 'window' at the same size as the SDL backbuffer. Although it
220      should never be drawn to directly, it would do no harm nevertheless. */
221
222   /* create additional (symbolic) buffer for double-buffering */
223   *window = CreateBitmap(video.width, video.height, video.depth);
224 }
225
226 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
227 {
228   boolean success = TRUE;
229   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
230   int surface_flags_window = SURFACE_FLAGS;
231   SDL_Surface *new_surface = NULL;
232
233   if (*backbuffer == NULL)
234     *backbuffer = CreateBitmapStruct();
235
236   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
237   {
238     setFullscreenParameters();
239
240     video_xoffset = fullscreen_xoffset;
241     video_yoffset = fullscreen_yoffset;
242
243     /* switch display to fullscreen mode, if available */
244     if ((new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
245                                         video.depth, surface_flags_fullscreen))
246         == NULL)
247     {
248       /* switching display to fullscreen mode failed */
249       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
250
251       /* do not try it again */
252       video.fullscreen_available = FALSE;
253       success = FALSE;
254     }
255     else
256     {
257       (*backbuffer)->surface = new_surface;
258
259       video.fullscreen_enabled = TRUE;
260       success = TRUE;
261     }
262   }
263
264   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
265   {
266     video_xoffset = 0;
267     video_yoffset = 0;
268
269     /* switch display to window mode */
270     if ((new_surface = SDL_SetVideoMode(video.width, video.height,
271                                         video.depth, surface_flags_window))
272         == NULL)
273     {
274       /* switching display to window mode failed -- should not happen */
275       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
276
277       success = FALSE;
278     }
279     else
280     {
281       (*backbuffer)->surface = new_surface;
282
283       video.fullscreen_enabled = FALSE;
284       success = TRUE;
285     }
286   }
287
288   return success;
289 }
290
291 void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
292                             int depth)
293 {
294   SDL_Surface *surface_tmp, *surface_native;
295
296   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
297                                           0, 0, 0, 0))
298       == NULL)
299     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
300
301   if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
302     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
303
304   SDL_FreeSurface(surface_tmp);
305
306   new_bitmap->surface = surface_native;
307 }
308
309 void SDLFreeBitmapPointers(Bitmap *bitmap)
310 {
311   if (bitmap->surface)
312     SDL_FreeSurface(bitmap->surface);
313   if (bitmap->surface_masked)
314     SDL_FreeSurface(bitmap->surface_masked);
315   bitmap->surface = NULL;
316   bitmap->surface_masked = NULL;
317 }
318
319 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
320                  int src_x, int src_y, int width, int height,
321                  int dst_x, int dst_y, int mask_mode)
322 {
323   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
324   SDL_Rect src_rect, dst_rect;
325
326   if (src_bitmap == backbuffer)
327   {
328     src_x += video_xoffset;
329     src_y += video_yoffset;
330   }
331
332   src_rect.x = src_x;
333   src_rect.y = src_y;
334   src_rect.w = width;
335   src_rect.h = height;
336
337   if (dst_bitmap == backbuffer || dst_bitmap == window)
338   {
339     dst_x += video_xoffset;
340     dst_y += video_yoffset;
341   }
342
343   dst_rect.x = dst_x;
344   dst_rect.y = dst_y;
345   dst_rect.w = width;
346   dst_rect.h = height;
347
348   if (src_bitmap != backbuffer || dst_bitmap != window)
349     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
350                      src_bitmap->surface_masked : src_bitmap->surface),
351                     &src_rect, real_dst_bitmap->surface, &dst_rect);
352
353   if (dst_bitmap == window)
354     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
355 }
356
357 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
358                       Uint32 color)
359 {
360   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
361   SDL_Rect rect;
362
363   if (dst_bitmap == backbuffer || dst_bitmap == window)
364   {
365     x += video_xoffset;
366     y += video_yoffset;
367   }
368
369   rect.x = x;
370   rect.y = y;
371   rect.w = width;
372   rect.h = height;
373
374   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
375
376   if (dst_bitmap == window)
377     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
378 }
379
380 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
381                       int fade_mode, int fade_delay, int post_delay)
382 {
383   static boolean initialization_needed = TRUE;
384   static SDL_Surface *surface_source = NULL;
385   static SDL_Surface *surface_target = NULL;
386   static SDL_Surface *surface_black = NULL;
387   SDL_Surface *surface_screen = backbuffer->surface;
388   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
389   SDL_Rect src_rect, dst_rect;
390   int src_x = x, src_y = y;
391   int dst_x = x, dst_y = y;
392   boolean fade_reverse = (fade_mode == FADE_MODE_FADE_IN ? TRUE : FALSE);
393   unsigned int time_last, time_current;
394   float alpha;
395   int alpha_final;
396
397   src_rect.x = src_x;
398   src_rect.y = src_y;
399   src_rect.w = width;
400   src_rect.h = height;
401
402   dst_x += video_xoffset;
403   dst_y += video_yoffset;
404
405   dst_rect.x = dst_x;
406   dst_rect.y = dst_y;
407   dst_rect.w = width;
408   dst_rect.h = height;
409
410   if (initialization_needed)
411   {
412     unsigned int flags = SDL_SRCALPHA;
413
414     /* use same surface type as screen surface */
415     if ((surface_screen->flags & SDL_HWSURFACE))
416       flags |= SDL_HWSURFACE;
417     else
418       flags |= SDL_SWSURFACE;
419
420     /* create surface for temporary copy of screen buffer (source) */
421     if ((surface_source =
422          SDL_CreateRGBSurface(flags,
423                               video.width,
424                               video.height,
425                               surface_screen->format->BitsPerPixel,
426                               surface_screen->format->Rmask,
427                               surface_screen->format->Gmask,
428                               surface_screen->format->Bmask,
429                               surface_screen->format->Amask)) == NULL)
430       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
431
432     /* create surface for cross-fading screen buffer (target) */
433     if ((surface_target =
434          SDL_CreateRGBSurface(flags,
435                               video.width,
436                               video.height,
437                               surface_screen->format->BitsPerPixel,
438                               surface_screen->format->Rmask,
439                               surface_screen->format->Gmask,
440                               surface_screen->format->Bmask,
441                               surface_screen->format->Amask)) == NULL)
442       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
443
444     /* create black surface for fading from/to black */
445     if ((surface_black =
446          SDL_CreateRGBSurface(flags,
447                               video.width,
448                               video.height,
449                               surface_screen->format->BitsPerPixel,
450                               surface_screen->format->Rmask,
451                               surface_screen->format->Gmask,
452                               surface_screen->format->Bmask,
453                               surface_screen->format->Amask)) == NULL)
454       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
455
456     /* completely fill the surface with black color pixels */
457     SDL_FillRect(surface_black, NULL,
458                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
459
460     initialization_needed = FALSE;
461   }
462
463   /* copy source and target surfaces to temporary surfaces for fading */
464   if (fade_mode == FADE_MODE_CROSSFADE)
465   {
466     SDL_BlitSurface(surface_cross, &src_rect, surface_source, &src_rect);
467     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
468   }
469   else
470   {
471     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
472     SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
473   }
474
475   time_current = SDL_GetTicks();
476
477   for (alpha = 0.0; alpha < 255.0;)
478   {
479     time_last = time_current;
480     time_current = SDL_GetTicks();
481     alpha += 255 * ((float)(time_current - time_last) / fade_delay);
482     alpha_final = (int)(fade_reverse ? 255.0 - alpha : alpha);
483     alpha_final = MIN(MAX(0, alpha_final), 255);
484
485     /* draw existing (source) image to screen buffer */
486     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
487
488     /* draw new (target) image to screen buffer using alpha blending */
489     SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
490     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
491
492 #if 1
493     /* only update the region of the screen that is affected from fading */
494     SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
495 #else
496     SDL_Flip(surface_screen);
497 #endif
498   }
499
500   Delay(post_delay);
501 }
502
503 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
504                        int to_x, int to_y, Uint32 color)
505 {
506   SDL_Surface *surface = dst_bitmap->surface;
507   SDL_Rect rect;
508
509   if (from_x > to_x)
510     swap_numbers(&from_x, &to_x);
511
512   if (from_y > to_y)
513     swap_numbers(&from_y, &to_y);
514
515   rect.x = from_x;
516   rect.y = from_y;
517   rect.w = (to_x - from_x + 1);
518   rect.h = (to_y - from_y + 1);
519
520   if (dst_bitmap == backbuffer || dst_bitmap == window)
521   {
522     rect.x += video_xoffset;
523     rect.y += video_yoffset;
524   }
525
526   SDL_FillRect(surface, &rect, color);
527 }
528
529 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
530                  int to_x, int to_y, Uint32 color)
531 {
532   if (dst_bitmap == backbuffer || dst_bitmap == window)
533   {
534     from_x += video_xoffset;
535     from_y += video_yoffset;
536     to_x += video_xoffset;
537     to_y += video_yoffset;
538   }
539
540   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
541 }
542
543 #if 0
544 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
545                   int num_points, Uint32 color)
546 {
547   int i, x, y;
548   int line_width = 4;
549
550   for (i = 0; i < num_points - 1; i++)
551   {
552     for (x = 0; x < line_width; x++)
553     {
554       for (y = 0; y < line_width; y++)
555       {
556         int dx = x - line_width / 2;
557         int dy = y - line_width / 2;
558
559         if ((x == 0 && y == 0) ||
560             (x == 0 && y == line_width - 1) ||
561             (x == line_width - 1 && y == 0) ||
562             (x == line_width - 1 && y == line_width - 1))
563           continue;
564
565         sge_Line(surface, points[i].x + dx, points[i].y + dy,
566                  points[i+1].x + dx, points[i+1].y + dy, color);
567       }
568     }
569   }
570 }
571 #endif
572
573 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
574 {
575   SDL_Surface *surface = src_bitmap->surface;
576
577   if (src_bitmap == backbuffer || src_bitmap == window)
578   {
579     x += video_xoffset;
580     y += video_yoffset;
581   }
582
583   switch (surface->format->BytesPerPixel)
584   {
585     case 1:             /* assuming 8-bpp */
586     {
587       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
588     }
589     break;
590
591     case 2:             /* probably 15-bpp or 16-bpp */
592     {
593       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
594     }
595     break;
596
597   case 3:               /* slow 24-bpp mode; usually not used */
598     {
599       /* does this work? */
600       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
601       Uint32 color = 0;
602       int shift;
603
604       shift = surface->format->Rshift;
605       color |= *(pix + shift / 8) >> shift;
606       shift = surface->format->Gshift;
607       color |= *(pix + shift / 8) >> shift;
608       shift = surface->format->Bshift;
609       color |= *(pix + shift / 8) >> shift;
610
611       return color;
612     }
613     break;
614
615   case 4:               /* probably 32-bpp */
616     {
617       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
618     }
619     break;
620   }
621
622   return 0;
623 }
624
625
626 /* ========================================================================= */
627 /* The following functions were taken from the SGE library                   */
628 /* (SDL Graphics Extension Library) by Anders Lindström                      */
629 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
630 /* ========================================================================= */
631
632 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
633 {
634   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
635   {
636     switch (surface->format->BytesPerPixel)
637     {
638       case 1:
639       {
640         /* Assuming 8-bpp */
641         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
642       }
643       break;
644
645       case 2:
646       {
647         /* Probably 15-bpp or 16-bpp */
648         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
649       }
650       break;
651
652       case 3:
653       {
654         /* Slow 24-bpp mode, usually not used */
655         Uint8 *pix;
656         int shift;
657
658         /* Gack - slow, but endian correct */
659         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
660         shift = surface->format->Rshift;
661         *(pix+shift/8) = color>>shift;
662         shift = surface->format->Gshift;
663         *(pix+shift/8) = color>>shift;
664         shift = surface->format->Bshift;
665         *(pix+shift/8) = color>>shift;
666       }
667       break;
668
669       case 4:
670       {
671         /* Probably 32-bpp */
672         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
673       }
674       break;
675     }
676   }
677 }
678
679 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
680                   Uint8 R, Uint8 G, Uint8 B)
681 {
682   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
683 }
684
685 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
686 {
687   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
688 }
689
690 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
691 {
692   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
693 }
694
695 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
696 {
697   Uint8 *pix;
698   int shift;
699
700   /* Gack - slow, but endian correct */
701   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
702   shift = surface->format->Rshift;
703   *(pix+shift/8) = color>>shift;
704   shift = surface->format->Gshift;
705   *(pix+shift/8) = color>>shift;
706   shift = surface->format->Bshift;
707   *(pix+shift/8) = color>>shift;
708 }
709
710 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
711 {
712   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
713 }
714
715 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
716 {
717   switch (dest->format->BytesPerPixel)
718   {
719     case 1:
720       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
721       break;
722
723     case 2:
724       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
725       break;
726
727     case 3:
728       _PutPixel24(dest,x,y,color);
729       break;
730
731     case 4:
732       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
733       break;
734   }
735 }
736
737 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
738 {
739   if (SDL_MUSTLOCK(surface))
740   {
741     if (SDL_LockSurface(surface) < 0)
742     {
743       return;
744     }
745   }
746
747   _PutPixel(surface, x, y, color);
748
749   if (SDL_MUSTLOCK(surface))
750   {
751     SDL_UnlockSurface(surface);
752   }
753 }
754
755 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
756                   Uint8 r, Uint8 g, Uint8 b)
757 {
758   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
759 }
760
761 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
762 {
763   if (y >= 0 && y <= dest->h - 1)
764   {
765     switch (dest->format->BytesPerPixel)
766     {
767       case 1:
768         return y*dest->pitch;
769         break;
770
771       case 2:
772         return y*dest->pitch/2;
773         break;
774
775       case 3:
776         return y*dest->pitch;
777         break;
778
779       case 4:
780         return y*dest->pitch/4;
781         break;
782     }
783   }
784
785   return -1;
786 }
787
788 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
789 {
790   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
791   {
792     switch (surface->format->BytesPerPixel)
793     {
794       case 1:
795       {
796         /* Assuming 8-bpp */
797         *((Uint8 *)surface->pixels + ypitch + x) = color;
798       }
799       break;
800
801       case 2:
802       {
803         /* Probably 15-bpp or 16-bpp */
804         *((Uint16 *)surface->pixels + ypitch + x) = color;
805       }
806       break;
807
808       case 3:
809       {
810         /* Slow 24-bpp mode, usually not used */
811         Uint8 *pix;
812         int shift;
813
814         /* Gack - slow, but endian correct */
815         pix = (Uint8 *)surface->pixels + ypitch + x*3;
816         shift = surface->format->Rshift;
817         *(pix+shift/8) = color>>shift;
818         shift = surface->format->Gshift;
819         *(pix+shift/8) = color>>shift;
820         shift = surface->format->Bshift;
821         *(pix+shift/8) = color>>shift;
822       }
823       break;
824
825       case 4:
826       {
827         /* Probably 32-bpp */
828         *((Uint32 *)surface->pixels + ypitch + x) = color;
829       }
830       break;
831     }
832   }
833 }
834
835 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
836                Uint32 Color)
837 {
838   SDL_Rect l;
839
840   if (SDL_MUSTLOCK(Surface))
841   {
842     if (SDL_LockSurface(Surface) < 0)
843     {
844       return;
845     }
846   }
847
848   if (x1 > x2)
849   {
850     Sint16 tmp = x1;
851     x1 = x2;
852     x2 = tmp;
853   }
854
855   /* Do the clipping */
856   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
857     return;
858   if (x1 < 0)
859     x1 = 0;
860   if (x2 > Surface->w - 1)
861     x2 = Surface->w - 1;
862
863   l.x = x1;
864   l.y = y;
865   l.w = x2 - x1 + 1;
866   l.h = 1;
867
868   SDL_FillRect(Surface, &l, Color);
869
870   if (SDL_MUSTLOCK(Surface))
871   {
872     SDL_UnlockSurface(Surface);
873   }
874 }
875
876 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
877                   Uint8 R, Uint8 G, Uint8 B)
878 {
879   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
880 }
881
882 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
883 {
884   SDL_Rect l;
885
886   if (x1 > x2)
887   {
888     Sint16 tmp = x1;
889     x1 = x2;
890     x2 = tmp;
891   }
892
893   /* Do the clipping */
894   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
895     return;
896   if (x1 < 0)
897     x1 = 0;
898   if (x2 > Surface->w - 1)
899     x2 = Surface->w - 1;
900
901   l.x = x1;
902   l.y = y;
903   l.w = x2 - x1 + 1;
904   l.h = 1;
905
906   SDL_FillRect(Surface, &l, Color);
907 }
908
909 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
910                Uint32 Color)
911 {
912   SDL_Rect l;
913
914   if (SDL_MUSTLOCK(Surface))
915   {
916     if (SDL_LockSurface(Surface) < 0)
917     {
918       return;
919     }
920   }
921
922   if (y1 > y2)
923   {
924     Sint16 tmp = y1;
925     y1 = y2;
926     y2 = tmp;
927   }
928
929   /* Do the clipping */
930   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
931     return;
932   if (y1 < 0)
933     y1 = 0;
934   if (y2 > Surface->h - 1)
935     y2 = Surface->h - 1;
936
937   l.x = x;
938   l.y = y1;
939   l.w = 1;
940   l.h = y2 - y1 + 1;
941
942   SDL_FillRect(Surface, &l, Color);
943
944   if (SDL_MUSTLOCK(Surface))
945   {
946     SDL_UnlockSurface(Surface);
947   }
948 }
949
950 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
951                   Uint8 R, Uint8 G, Uint8 B)
952 {
953   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
954 }
955
956 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
957 {
958   SDL_Rect l;
959
960   if (y1 > y2)
961   {
962     Sint16 tmp = y1;
963     y1 = y2;
964     y2 = tmp;
965   }
966
967   /* Do the clipping */
968   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
969     return;
970   if (y1 < 0)
971     y1 = 0;
972   if (y2 > Surface->h - 1)
973     y2 = Surface->h - 1;
974
975   l.x = x;
976   l.y = y1;
977   l.w = 1;
978   l.h = y2 - y1 + 1;
979
980   SDL_FillRect(Surface, &l, Color);
981 }
982
983 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
984                 Sint16 x2, Sint16 y2, Uint32 Color,
985                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
986                               Uint32 Color))
987 {
988   Sint16 dx, dy, sdx, sdy, x, y, px, py;
989
990   dx = x2 - x1;
991   dy = y2 - y1;
992
993   sdx = (dx < 0) ? -1 : 1;
994   sdy = (dy < 0) ? -1 : 1;
995
996   dx = sdx * dx + 1;
997   dy = sdy * dy + 1;
998
999   x = y = 0;
1000
1001   px = x1;
1002   py = y1;
1003
1004   if (dx >= dy)
1005   {
1006     for (x = 0; x < dx; x++)
1007     {
1008       Callback(Surface, px, py, Color);
1009
1010       y += dy;
1011       if (y >= dx)
1012       {
1013         y -= dx;
1014         py += sdy;
1015       }
1016
1017       px += sdx;
1018     }
1019   }
1020   else
1021   {
1022     for (y = 0; y < dy; y++)
1023     {
1024       Callback(Surface, px, py, Color);
1025
1026       x += dx;
1027       if (x >= dy)
1028       {
1029         x -= dy;
1030         px += sdx;
1031       }
1032
1033       py += sdy;
1034     }
1035   }
1036 }
1037
1038 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1039                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1040                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1041                                  Uint32 Color))
1042 {
1043   sge_DoLine(Surface, X1, Y1, X2, Y2,
1044              SDL_MapRGB(Surface->format, R, G, B), Callback);
1045 }
1046
1047 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1048               Uint32 Color)
1049 {
1050   if (SDL_MUSTLOCK(Surface))
1051   {
1052     if (SDL_LockSurface(Surface) < 0)
1053       return;
1054    }
1055
1056    /* Draw the line */
1057    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1058
1059    /* unlock the display */
1060    if (SDL_MUSTLOCK(Surface))
1061    {
1062       SDL_UnlockSurface(Surface);
1063    }
1064 }
1065
1066 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1067                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1068 {
1069   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1070 }
1071
1072 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1073 {
1074   if (dst_bitmap == backbuffer || dst_bitmap == window)
1075   {
1076     x += video_xoffset;
1077     y += video_yoffset;
1078   }
1079
1080   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1081 }
1082
1083
1084 /*
1085   -----------------------------------------------------------------------------
1086   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1087   -----------------------------------------------------------------------------
1088 */
1089
1090 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1091                    int width, int height, Uint32 color)
1092 {
1093   int x, y;
1094
1095   for (y = src_y; y < src_y + height; y++)
1096   {
1097     for (x = src_x; x < src_x + width; x++)
1098     {
1099       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1100
1101       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1102     }
1103   }
1104 }
1105
1106 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1107                           int src_x, int src_y, int width, int height,
1108                           int dst_x, int dst_y)
1109 {
1110   int x, y;
1111
1112   for (y = 0; y < height; y++)
1113   {
1114     for (x = 0; x < width; x++)
1115     {
1116       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1117
1118       if (pixel != BLACK_PIXEL)
1119         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1120     }
1121   }
1122 }
1123
1124
1125 /* ========================================================================= */
1126 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1127 /* (Rotozoomer) by Andreas Schiffler                                         */
1128 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1129 /* ========================================================================= */
1130
1131 /*
1132   -----------------------------------------------------------------------------
1133   32 bit zoomer
1134
1135   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1136   -----------------------------------------------------------------------------
1137 */
1138
1139 typedef struct
1140 {
1141   Uint8 r;
1142   Uint8 g;
1143   Uint8 b;
1144   Uint8 a;
1145 } tColorRGBA;
1146
1147 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1148 {
1149   int x, y;
1150   tColorRGBA *sp, *csp, *dp;
1151   int sgap, dgap;
1152
1153   /* pointer setup */
1154   sp = csp = (tColorRGBA *) src->pixels;
1155   dp = (tColorRGBA *) dst->pixels;
1156   sgap = src->pitch - src->w * 4;
1157   dgap = dst->pitch - dst->w * 4;
1158
1159   for (y = 0; y < dst->h; y++)
1160   {
1161     sp = csp;
1162
1163     for (x = 0; x < dst->w; x++)
1164     {
1165       tColorRGBA *sp0 = sp;
1166       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1167       tColorRGBA *sp00 = &sp0[0];
1168       tColorRGBA *sp01 = &sp0[1];
1169       tColorRGBA *sp10 = &sp1[0];
1170       tColorRGBA *sp11 = &sp1[1];
1171       tColorRGBA new;
1172
1173       /* create new color pixel from all four source color pixels */
1174       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1175       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1176       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1177       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1178
1179       /* draw */
1180       *dp = new;
1181
1182       /* advance source pointers */
1183       sp += 2;
1184
1185       /* advance destination pointer */
1186       dp++;
1187     }
1188
1189     /* advance source pointer */
1190     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1191
1192     /* advance destination pointers */
1193     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1194   }
1195
1196   return 0;
1197 }
1198
1199 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1200 {
1201   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1202   tColorRGBA *sp, *csp, *dp;
1203   int sgap, dgap;
1204
1205   /* use specialized zoom function when scaling down to exactly half size */
1206   if (src->w == 2 * dst->w &&
1207       src->h == 2 * dst->h)
1208     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1209
1210   /* variable setup */
1211   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1212   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1213
1214   /* allocate memory for row increments */
1215   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1216   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1217
1218   /* precalculate row increments */
1219   csx = 0;
1220   csax = sax;
1221   for (x = 0; x <= dst->w; x++)
1222   {
1223     *csax = csx;
1224     csax++;
1225     csx &= 0xffff;
1226     csx += sx;
1227   }
1228
1229   csy = 0;
1230   csay = say;
1231   for (y = 0; y <= dst->h; y++)
1232   {
1233     *csay = csy;
1234     csay++;
1235     csy &= 0xffff;
1236     csy += sy;
1237   }
1238
1239   /* pointer setup */
1240   sp = csp = (tColorRGBA *) src->pixels;
1241   dp = (tColorRGBA *) dst->pixels;
1242   sgap = src->pitch - src->w * 4;
1243   dgap = dst->pitch - dst->w * 4;
1244
1245   csay = say;
1246   for (y = 0; y < dst->h; y++)
1247   {
1248     sp = csp;
1249     csax = sax;
1250
1251     for (x = 0; x < dst->w; x++)
1252     {
1253       /* draw */
1254       *dp = *sp;
1255
1256       /* advance source pointers */
1257       csax++;
1258       sp += (*csax >> 16);
1259
1260       /* advance destination pointer */
1261       dp++;
1262     }
1263
1264     /* advance source pointer */
1265     csay++;
1266     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1267
1268     /* advance destination pointers */
1269     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1270   }
1271
1272   free(sax);
1273   free(say);
1274
1275   return 0;
1276 }
1277
1278 /*
1279   -----------------------------------------------------------------------------
1280   8 bit zoomer
1281
1282   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1283   -----------------------------------------------------------------------------
1284 */
1285
1286 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1287 {
1288   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1289   Uint8 *sp, *dp, *csp;
1290   int dgap;
1291
1292   /* variable setup */
1293   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1294   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1295
1296   /* allocate memory for row increments */
1297   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1298   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1299
1300   /* precalculate row increments */
1301   csx = 0;
1302   csax = sax;
1303   for (x = 0; x < dst->w; x++)
1304   {
1305     csx += sx;
1306     *csax = (csx >> 16);
1307     csx &= 0xffff;
1308     csax++;
1309   }
1310
1311   csy = 0;
1312   csay = say;
1313   for (y = 0; y < dst->h; y++)
1314   {
1315     csy += sy;
1316     *csay = (csy >> 16);
1317     csy &= 0xffff;
1318     csay++;
1319   }
1320
1321   csx = 0;
1322   csax = sax;
1323   for (x = 0; x < dst->w; x++)
1324   {
1325     csx += (*csax);
1326     csax++;
1327   }
1328
1329   csy = 0;
1330   csay = say;
1331   for (y = 0; y < dst->h; y++)
1332   {
1333     csy += (*csay);
1334     csay++;
1335   }
1336
1337   /* pointer setup */
1338   sp = csp = (Uint8 *) src->pixels;
1339   dp = (Uint8 *) dst->pixels;
1340   dgap = dst->pitch - dst->w;
1341
1342   /* draw */
1343   csay = say;
1344   for (y = 0; y < dst->h; y++)
1345   {
1346     csax = sax;
1347     sp = csp;
1348     for (x = 0; x < dst->w; x++)
1349     {
1350       /* draw */
1351       *dp = *sp;
1352
1353       /* advance source pointers */
1354       sp += (*csax);
1355       csax++;
1356
1357       /* advance destination pointer */
1358       dp++;
1359     }
1360
1361     /* advance source pointer (for row) */
1362     csp += ((*csay) * src->pitch);
1363     csay++;
1364
1365     /* advance destination pointers */
1366     dp += dgap;
1367   }
1368
1369   free(sax);
1370   free(say);
1371
1372   return 0;
1373 }
1374
1375 /*
1376   -----------------------------------------------------------------------------
1377   zoomSurface()
1378
1379   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1380   'zoomx' and 'zoomy' are scaling factors for width and height.
1381   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1382   into a 32bit RGBA format on the fly.
1383   -----------------------------------------------------------------------------
1384 */
1385
1386 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1387 {
1388   SDL_Surface *zoom_src = NULL;
1389   SDL_Surface *zoom_dst = NULL;
1390   boolean is_converted = FALSE;
1391   boolean is_32bit;
1392   int i;
1393
1394   if (src == NULL)
1395     return NULL;
1396
1397   /* determine if source surface is 32 bit or 8 bit */
1398   is_32bit = (src->format->BitsPerPixel == 32);
1399
1400   if (is_32bit || src->format->BitsPerPixel == 8)
1401   {
1402     /* use source surface 'as is' */
1403     zoom_src = src;
1404   }
1405   else
1406   {
1407     /* new source surface is 32 bit with a defined RGB ordering */
1408     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1409                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1410     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1411     is_32bit = TRUE;
1412     is_converted = TRUE;
1413   }
1414
1415   /* allocate surface to completely contain the zoomed surface */
1416   if (is_32bit)
1417   {
1418     /* target surface is 32 bit with source RGBA/ABGR ordering */
1419     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1420                                     zoom_src->format->Rmask,
1421                                     zoom_src->format->Gmask,
1422                                     zoom_src->format->Bmask, 0);
1423   }
1424   else
1425   {
1426     /* target surface is 8 bit */
1427     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1428                                     0, 0, 0, 0);
1429   }
1430
1431   /* lock source surface */
1432   SDL_LockSurface(zoom_src);
1433
1434   /* check which kind of surface we have */
1435   if (is_32bit)
1436   {
1437     /* call the 32 bit transformation routine to do the zooming */
1438     zoomSurfaceRGBA(zoom_src, zoom_dst);
1439   }
1440   else
1441   {
1442     /* copy palette */
1443     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1444       zoom_dst->format->palette->colors[i] =
1445         zoom_src->format->palette->colors[i];
1446     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1447
1448     /* call the 8 bit transformation routine to do the zooming */
1449     zoomSurfaceY(zoom_src, zoom_dst);
1450   }
1451
1452   /* unlock source surface */
1453   SDL_UnlockSurface(zoom_src);
1454
1455   /* free temporary surface */
1456   if (is_converted)
1457     SDL_FreeSurface(zoom_src);
1458
1459   /* return destination surface */
1460   return zoom_dst;
1461 }
1462
1463 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1464 {
1465   SDL_Surface *sdl_surface_tmp;
1466   int dst_width = dst_bitmap->width;
1467   int dst_height = dst_bitmap->height;
1468
1469   /* throw away old destination surface */
1470   SDL_FreeSurface(dst_bitmap->surface);
1471
1472   /* create zoomed temporary surface from source surface */
1473   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1474
1475   /* create native format destination surface from zoomed temporary surface */
1476   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1477
1478   /* free temporary surface */
1479   SDL_FreeSurface(sdl_surface_tmp);
1480 }
1481
1482
1483 /* ========================================================================= */
1484 /* load image to bitmap                                                      */
1485 /* ========================================================================= */
1486
1487 Bitmap *SDLLoadImage(char *filename)
1488 {
1489   Bitmap *new_bitmap = CreateBitmapStruct();
1490   SDL_Surface *sdl_image_tmp;
1491
1492   /* load image to temporary surface */
1493   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1494   {
1495     SetError("IMG_Load(): %s", SDL_GetError());
1496
1497     return NULL;
1498   }
1499
1500   /* create native non-transparent surface for current image */
1501   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1502   {
1503     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1504
1505     return NULL;
1506   }
1507
1508   /* create native transparent surface for current image */
1509   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1510                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1511   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1512   {
1513     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1514
1515     return NULL;
1516   }
1517
1518   /* free temporary surface */
1519   SDL_FreeSurface(sdl_image_tmp);
1520
1521   new_bitmap->width = new_bitmap->surface->w;
1522   new_bitmap->height = new_bitmap->surface->h;
1523
1524   return new_bitmap;
1525 }
1526
1527
1528 /* ------------------------------------------------------------------------- */
1529 /* custom cursor fuctions                                                    */
1530 /* ------------------------------------------------------------------------- */
1531
1532 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1533 {
1534   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1535                           cursor_info->width, cursor_info->height,
1536                           cursor_info->hot_x, cursor_info->hot_y);
1537 }
1538
1539 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1540 {
1541   static struct MouseCursorInfo *last_cursor_info = NULL;
1542   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1543   static SDL_Cursor *cursor_default = NULL;
1544   static SDL_Cursor *cursor_current = NULL;
1545
1546   /* if invoked for the first time, store the SDL default cursor */
1547   if (cursor_default == NULL)
1548     cursor_default = SDL_GetCursor();
1549
1550   /* only create new cursor if cursor info (custom only) has changed */
1551   if (cursor_info != NULL && cursor_info != last_cursor_info)
1552   {
1553     cursor_current = create_cursor(cursor_info);
1554     last_cursor_info = cursor_info;
1555   }
1556
1557   /* only set new cursor if cursor info (custom or NULL) has changed */
1558   if (cursor_info != last_cursor_info2)
1559     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1560
1561   last_cursor_info2 = cursor_info;
1562 }
1563
1564
1565 /* ========================================================================= */
1566 /* audio functions                                                           */
1567 /* ========================================================================= */
1568
1569 void SDLOpenAudio(void)
1570 {
1571   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1572     putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1573
1574   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1575   {
1576     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1577     return;
1578   }
1579
1580   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1581                     AUDIO_NUM_CHANNELS_STEREO,
1582                     setup.system.audio_fragment_size) < 0)
1583   {
1584     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1585     return;
1586   }
1587
1588   audio.sound_available = TRUE;
1589   audio.music_available = TRUE;
1590   audio.loops_available = TRUE;
1591   audio.sound_enabled = TRUE;
1592
1593   /* set number of available mixer channels */
1594   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1595   audio.music_channel = MUSIC_CHANNEL;
1596   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1597
1598   Mixer_InitChannels();
1599 }
1600
1601 void SDLCloseAudio(void)
1602 {
1603   Mix_HaltMusic();
1604   Mix_HaltChannel(-1);
1605
1606   Mix_CloseAudio();
1607   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1608 }
1609
1610
1611 /* ========================================================================= */
1612 /* event functions                                                           */
1613 /* ========================================================================= */
1614
1615 void SDLNextEvent(Event *event)
1616 {
1617   SDL_WaitEvent(event);
1618
1619   if (event->type == EVENT_BUTTONPRESS ||
1620       event->type == EVENT_BUTTONRELEASE)
1621   {
1622     if (((ButtonEvent *)event)->x > video_xoffset)
1623       ((ButtonEvent *)event)->x -= video_xoffset;
1624     else
1625       ((ButtonEvent *)event)->x = 0;
1626     if (((ButtonEvent *)event)->y > video_yoffset)
1627       ((ButtonEvent *)event)->y -= video_yoffset;
1628     else
1629       ((ButtonEvent *)event)->y = 0;
1630   }
1631   else if (event->type == EVENT_MOTIONNOTIFY)
1632   {
1633     if (((MotionEvent *)event)->x > video_xoffset)
1634       ((MotionEvent *)event)->x -= video_xoffset;
1635     else
1636       ((MotionEvent *)event)->x = 0;
1637     if (((MotionEvent *)event)->y > video_yoffset)
1638       ((MotionEvent *)event)->y -= video_yoffset;
1639     else
1640       ((MotionEvent *)event)->y = 0;
1641   }
1642 }
1643
1644
1645 /* ========================================================================= */
1646 /* joystick functions                                                        */
1647 /* ========================================================================= */
1648
1649 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1650 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1651 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1652
1653 static boolean SDLOpenJoystick(int nr)
1654 {
1655   if (nr < 0 || nr > MAX_PLAYERS)
1656     return FALSE;
1657
1658   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1659 }
1660
1661 static void SDLCloseJoystick(int nr)
1662 {
1663   if (nr < 0 || nr > MAX_PLAYERS)
1664     return;
1665
1666   SDL_JoystickClose(sdl_joystick[nr]);
1667 }
1668
1669 static boolean SDLCheckJoystickOpened(int nr)
1670 {
1671   if (nr < 0 || nr > MAX_PLAYERS)
1672     return FALSE;
1673
1674   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1675 }
1676
1677 void HandleJoystickEvent(Event *event)
1678 {
1679   switch(event->type)
1680   {
1681     case SDL_JOYAXISMOTION:
1682       if (event->jaxis.axis < 2)
1683         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1684       break;
1685
1686     case SDL_JOYBUTTONDOWN:
1687       if (event->jbutton.button < 2)
1688         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1689       break;
1690
1691     case SDL_JOYBUTTONUP:
1692       if (event->jbutton.button < 2)
1693         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1694       break;
1695
1696     default:
1697       break;
1698   }
1699 }
1700
1701 void SDLInitJoysticks()
1702 {
1703   static boolean sdl_joystick_subsystem_initialized = FALSE;
1704   boolean print_warning = !sdl_joystick_subsystem_initialized;
1705   int i;
1706
1707   if (!sdl_joystick_subsystem_initialized)
1708   {
1709     sdl_joystick_subsystem_initialized = TRUE;
1710
1711     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1712     {
1713       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1714       return;
1715     }
1716   }
1717
1718   for (i = 0; i < MAX_PLAYERS; i++)
1719   {
1720     /* get configured joystick for this player */
1721     char *device_name = setup.input[i].joy.device_name;
1722     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1723
1724     if (joystick_nr >= SDL_NumJoysticks())
1725     {
1726       if (setup.input[i].use_joystick && print_warning)
1727         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1728
1729       joystick_nr = -1;
1730     }
1731
1732     /* misuse joystick file descriptor variable to store joystick number */
1733     joystick.fd[i] = joystick_nr;
1734
1735     if (joystick_nr == -1)
1736       continue;
1737
1738     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1739     if (SDLCheckJoystickOpened(joystick_nr))
1740       SDLCloseJoystick(joystick_nr);
1741
1742     if (!setup.input[i].use_joystick)
1743       continue;
1744
1745     if (!SDLOpenJoystick(joystick_nr))
1746     {
1747       if (print_warning)
1748         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1749
1750       continue;
1751     }
1752
1753     joystick.status = JOYSTICK_ACTIVATED;
1754   }
1755 }
1756
1757 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1758 {
1759   if (nr < 0 || nr >= MAX_PLAYERS)
1760     return FALSE;
1761
1762   if (x != NULL)
1763     *x = sdl_js_axis[nr][0];
1764   if (y != NULL)
1765     *y = sdl_js_axis[nr][1];
1766
1767   if (b1 != NULL)
1768     *b1 = sdl_js_button[nr][0];
1769   if (b2 != NULL)
1770     *b2 = sdl_js_button[nr][1];
1771
1772   return TRUE;
1773 }
1774
1775 #endif /* TARGET_SDL */