rnd-20060816-1-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_screen_copy = NULL;
385   static SDL_Surface *surface_black = NULL;
386   SDL_Surface *surface_screen = backbuffer->surface;
387   SDL_Surface *surface_cross;           /* initialized later */
388   SDL_Rect src_rect, dst_rect;
389   int src_x = x, src_y = y;
390   int dst_x = x, dst_y = y;
391   boolean fade_reverse = (fade_mode == FADE_MODE_FADE_IN ? TRUE : FALSE);
392   unsigned int time_last, time_current;
393   float alpha;
394   int alpha_final;
395
396   src_rect.x = src_x;
397   src_rect.y = src_y;
398   src_rect.w = width;
399   src_rect.h = height;
400
401   dst_x += video_xoffset;
402   dst_y += video_yoffset;
403
404   dst_rect.x = dst_x;
405   dst_rect.y = dst_y;
406   dst_rect.w = width;
407   dst_rect.h = height;
408
409 #if 0
410   if (!initialization_needed)
411   {
412     /* check if screen size has changed (can happen when toggling fullscreen) */
413     if (surface_screen_copy->w != surface_screen->w ||
414         surface_screen_copy->h != surface_screen->h)
415     {
416       SDL_FreeSurface(surface_screen_copy);
417       SDL_FreeSurface(surface_black);
418
419       initialization_needed = TRUE;
420     }
421   }
422 #endif
423
424   if (initialization_needed)
425   {
426     unsigned int flags = SDL_SRCALPHA;
427
428     /* use same surface type as screen surface */
429     if ((surface_screen->flags & SDL_HWSURFACE))
430       flags |= SDL_HWSURFACE;
431     else
432       flags |= SDL_SWSURFACE;
433
434     /* create surface for temporary copy of screen buffer */
435     if ((surface_screen_copy =
436          SDL_CreateRGBSurface(flags,
437 #if 1
438                               video.width,
439                               video.height,
440 #else
441                               surface_screen->w,
442                               surface_screen->h,
443 #endif
444                               surface_screen->format->BitsPerPixel,
445                               surface_screen->format->Rmask,
446                               surface_screen->format->Gmask,
447                               surface_screen->format->Bmask,
448                               surface_screen->format->Amask)) == NULL)
449       Error(ERR_EXIT, "SDL_CreateRGBSurface(    ) failed: %s", SDL_GetError());
450
451     /* create black surface for fading from/to black */
452     if ((surface_black =
453          SDL_CreateRGBSurface(flags,
454 #if 1
455                               video.width,
456                               video.height,
457 #else
458                               surface_screen->w,
459                               surface_screen->h,
460 #endif
461                               surface_screen->format->BitsPerPixel,
462                               surface_screen->format->Rmask,
463                               surface_screen->format->Gmask,
464                               surface_screen->format->Bmask,
465                               surface_screen->format->Amask)) == NULL)
466       Error(ERR_EXIT, "SDL_CreateRGBSurface(    ) failed: %s", SDL_GetError());
467
468     /* completely fill the surface with black color pixels */
469     SDL_FillRect(surface_black, NULL,
470                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
471
472     initialization_needed = FALSE;
473   }
474
475   /* copy the current screen backbuffer to the temporary screen copy buffer */
476   SDL_BlitSurface(surface_screen, &dst_rect, surface_screen_copy, &src_rect);
477
478   surface_cross = (fade_mode == FADE_MODE_CROSSFADE ? bitmap_cross->surface :
479                    surface_black);
480
481   time_current = SDL_GetTicks();
482
483   for (alpha = 0.0; alpha < 255.0;)
484   {
485     time_last = time_current;
486     time_current = SDL_GetTicks();
487     alpha += 255 * ((float)(time_current - time_last) / fade_delay);
488     alpha_final = (int)(fade_reverse ? 255.0 - alpha : alpha);
489     alpha_final = MIN(MAX(0, alpha_final), 255);
490
491     /* draw existing image to screen buffer */
492     SDL_BlitSurface(surface_screen_copy, &src_rect, surface_screen, &dst_rect);
493
494     /* draw new image to screen buffer using alpha blending */
495     SDL_SetAlpha(surface_cross, SDL_SRCALPHA, alpha_final);
496     SDL_BlitSurface(surface_cross, &src_rect, surface_screen, &dst_rect);
497
498     /* draw screen buffer to visible display */
499 #if 1
500     SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
501 #else
502     SDL_Flip(surface_screen);
503 #endif
504   }
505
506   Delay(post_delay);
507 }
508
509 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
510                        int to_x, int to_y, Uint32 color)
511 {
512   SDL_Surface *surface = dst_bitmap->surface;
513   SDL_Rect rect;
514
515   if (from_x > to_x)
516     swap_numbers(&from_x, &to_x);
517
518   if (from_y > to_y)
519     swap_numbers(&from_y, &to_y);
520
521   rect.x = from_x;
522   rect.y = from_y;
523   rect.w = (to_x - from_x + 1);
524   rect.h = (to_y - from_y + 1);
525
526   if (dst_bitmap == backbuffer || dst_bitmap == window)
527   {
528     rect.x += video_xoffset;
529     rect.y += video_yoffset;
530   }
531
532   SDL_FillRect(surface, &rect, color);
533 }
534
535 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
536                  int to_x, int to_y, Uint32 color)
537 {
538   if (dst_bitmap == backbuffer || dst_bitmap == window)
539   {
540     from_x += video_xoffset;
541     from_y += video_yoffset;
542     to_x += video_xoffset;
543     to_y += video_yoffset;
544   }
545
546   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
547 }
548
549 #if 0
550 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
551                   int num_points, Uint32 color)
552 {
553   int i, x, y;
554   int line_width = 4;
555
556   for (i = 0; i < num_points - 1; i++)
557   {
558     for (x = 0; x < line_width; x++)
559     {
560       for (y = 0; y < line_width; y++)
561       {
562         int dx = x - line_width / 2;
563         int dy = y - line_width / 2;
564
565         if ((x == 0 && y == 0) ||
566             (x == 0 && y == line_width - 1) ||
567             (x == line_width - 1 && y == 0) ||
568             (x == line_width - 1 && y == line_width - 1))
569           continue;
570
571         sge_Line(surface, points[i].x + dx, points[i].y + dy,
572                  points[i+1].x + dx, points[i+1].y + dy, color);
573       }
574     }
575   }
576 }
577 #endif
578
579 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
580 {
581   SDL_Surface *surface = src_bitmap->surface;
582
583   if (src_bitmap == backbuffer || src_bitmap == window)
584   {
585     x += video_xoffset;
586     y += video_yoffset;
587   }
588
589   switch (surface->format->BytesPerPixel)
590   {
591     case 1:             /* assuming 8-bpp */
592     {
593       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
594     }
595     break;
596
597     case 2:             /* probably 15-bpp or 16-bpp */
598     {
599       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
600     }
601     break;
602
603   case 3:               /* slow 24-bpp mode; usually not used */
604     {
605       /* does this work? */
606       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
607       Uint32 color = 0;
608       int shift;
609
610       shift = surface->format->Rshift;
611       color |= *(pix + shift / 8) >> shift;
612       shift = surface->format->Gshift;
613       color |= *(pix + shift / 8) >> shift;
614       shift = surface->format->Bshift;
615       color |= *(pix + shift / 8) >> shift;
616
617       return color;
618     }
619     break;
620
621   case 4:               /* probably 32-bpp */
622     {
623       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
624     }
625     break;
626   }
627
628   return 0;
629 }
630
631
632 /* ========================================================================= */
633 /* The following functions were taken from the SGE library                   */
634 /* (SDL Graphics Extension Library) by Anders Lindström                      */
635 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
636 /* ========================================================================= */
637
638 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
639 {
640   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
641   {
642     switch (surface->format->BytesPerPixel)
643     {
644       case 1:
645       {
646         /* Assuming 8-bpp */
647         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
648       }
649       break;
650
651       case 2:
652       {
653         /* Probably 15-bpp or 16-bpp */
654         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
655       }
656       break;
657
658       case 3:
659       {
660         /* Slow 24-bpp mode, usually not used */
661         Uint8 *pix;
662         int shift;
663
664         /* Gack - slow, but endian correct */
665         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
666         shift = surface->format->Rshift;
667         *(pix+shift/8) = color>>shift;
668         shift = surface->format->Gshift;
669         *(pix+shift/8) = color>>shift;
670         shift = surface->format->Bshift;
671         *(pix+shift/8) = color>>shift;
672       }
673       break;
674
675       case 4:
676       {
677         /* Probably 32-bpp */
678         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
679       }
680       break;
681     }
682   }
683 }
684
685 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
686                   Uint8 R, Uint8 G, Uint8 B)
687 {
688   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
689 }
690
691 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
692 {
693   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
694 }
695
696 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
697 {
698   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
699 }
700
701 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
702 {
703   Uint8 *pix;
704   int shift;
705
706   /* Gack - slow, but endian correct */
707   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
708   shift = surface->format->Rshift;
709   *(pix+shift/8) = color>>shift;
710   shift = surface->format->Gshift;
711   *(pix+shift/8) = color>>shift;
712   shift = surface->format->Bshift;
713   *(pix+shift/8) = color>>shift;
714 }
715
716 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
717 {
718   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
719 }
720
721 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
722 {
723   switch (dest->format->BytesPerPixel)
724   {
725     case 1:
726       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
727       break;
728
729     case 2:
730       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
731       break;
732
733     case 3:
734       _PutPixel24(dest,x,y,color);
735       break;
736
737     case 4:
738       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
739       break;
740   }
741 }
742
743 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
744 {
745   if (SDL_MUSTLOCK(surface))
746   {
747     if (SDL_LockSurface(surface) < 0)
748     {
749       return;
750     }
751   }
752
753   _PutPixel(surface, x, y, color);
754
755   if (SDL_MUSTLOCK(surface))
756   {
757     SDL_UnlockSurface(surface);
758   }
759 }
760
761 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
762                   Uint8 r, Uint8 g, Uint8 b)
763 {
764   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
765 }
766
767 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
768 {
769   if (y >= 0 && y <= dest->h - 1)
770   {
771     switch (dest->format->BytesPerPixel)
772     {
773       case 1:
774         return y*dest->pitch;
775         break;
776
777       case 2:
778         return y*dest->pitch/2;
779         break;
780
781       case 3:
782         return y*dest->pitch;
783         break;
784
785       case 4:
786         return y*dest->pitch/4;
787         break;
788     }
789   }
790
791   return -1;
792 }
793
794 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
795 {
796   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
797   {
798     switch (surface->format->BytesPerPixel)
799     {
800       case 1:
801       {
802         /* Assuming 8-bpp */
803         *((Uint8 *)surface->pixels + ypitch + x) = color;
804       }
805       break;
806
807       case 2:
808       {
809         /* Probably 15-bpp or 16-bpp */
810         *((Uint16 *)surface->pixels + ypitch + x) = color;
811       }
812       break;
813
814       case 3:
815       {
816         /* Slow 24-bpp mode, usually not used */
817         Uint8 *pix;
818         int shift;
819
820         /* Gack - slow, but endian correct */
821         pix = (Uint8 *)surface->pixels + ypitch + x*3;
822         shift = surface->format->Rshift;
823         *(pix+shift/8) = color>>shift;
824         shift = surface->format->Gshift;
825         *(pix+shift/8) = color>>shift;
826         shift = surface->format->Bshift;
827         *(pix+shift/8) = color>>shift;
828       }
829       break;
830
831       case 4:
832       {
833         /* Probably 32-bpp */
834         *((Uint32 *)surface->pixels + ypitch + x) = color;
835       }
836       break;
837     }
838   }
839 }
840
841 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
842                Uint32 Color)
843 {
844   SDL_Rect l;
845
846   if (SDL_MUSTLOCK(Surface))
847   {
848     if (SDL_LockSurface(Surface) < 0)
849     {
850       return;
851     }
852   }
853
854   if (x1 > x2)
855   {
856     Sint16 tmp = x1;
857     x1 = x2;
858     x2 = tmp;
859   }
860
861   /* Do the clipping */
862   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
863     return;
864   if (x1 < 0)
865     x1 = 0;
866   if (x2 > Surface->w - 1)
867     x2 = Surface->w - 1;
868
869   l.x = x1;
870   l.y = y;
871   l.w = x2 - x1 + 1;
872   l.h = 1;
873
874   SDL_FillRect(Surface, &l, Color);
875
876   if (SDL_MUSTLOCK(Surface))
877   {
878     SDL_UnlockSurface(Surface);
879   }
880 }
881
882 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
883                   Uint8 R, Uint8 G, Uint8 B)
884 {
885   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
886 }
887
888 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
889 {
890   SDL_Rect l;
891
892   if (x1 > x2)
893   {
894     Sint16 tmp = x1;
895     x1 = x2;
896     x2 = tmp;
897   }
898
899   /* Do the clipping */
900   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
901     return;
902   if (x1 < 0)
903     x1 = 0;
904   if (x2 > Surface->w - 1)
905     x2 = Surface->w - 1;
906
907   l.x = x1;
908   l.y = y;
909   l.w = x2 - x1 + 1;
910   l.h = 1;
911
912   SDL_FillRect(Surface, &l, Color);
913 }
914
915 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
916                Uint32 Color)
917 {
918   SDL_Rect l;
919
920   if (SDL_MUSTLOCK(Surface))
921   {
922     if (SDL_LockSurface(Surface) < 0)
923     {
924       return;
925     }
926   }
927
928   if (y1 > y2)
929   {
930     Sint16 tmp = y1;
931     y1 = y2;
932     y2 = tmp;
933   }
934
935   /* Do the clipping */
936   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
937     return;
938   if (y1 < 0)
939     y1 = 0;
940   if (y2 > Surface->h - 1)
941     y2 = Surface->h - 1;
942
943   l.x = x;
944   l.y = y1;
945   l.w = 1;
946   l.h = y2 - y1 + 1;
947
948   SDL_FillRect(Surface, &l, Color);
949
950   if (SDL_MUSTLOCK(Surface))
951   {
952     SDL_UnlockSurface(Surface);
953   }
954 }
955
956 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
957                   Uint8 R, Uint8 G, Uint8 B)
958 {
959   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
960 }
961
962 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
963 {
964   SDL_Rect l;
965
966   if (y1 > y2)
967   {
968     Sint16 tmp = y1;
969     y1 = y2;
970     y2 = tmp;
971   }
972
973   /* Do the clipping */
974   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
975     return;
976   if (y1 < 0)
977     y1 = 0;
978   if (y2 > Surface->h - 1)
979     y2 = Surface->h - 1;
980
981   l.x = x;
982   l.y = y1;
983   l.w = 1;
984   l.h = y2 - y1 + 1;
985
986   SDL_FillRect(Surface, &l, Color);
987 }
988
989 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
990                 Sint16 x2, Sint16 y2, Uint32 Color,
991                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
992                               Uint32 Color))
993 {
994   Sint16 dx, dy, sdx, sdy, x, y, px, py;
995
996   dx = x2 - x1;
997   dy = y2 - y1;
998
999   sdx = (dx < 0) ? -1 : 1;
1000   sdy = (dy < 0) ? -1 : 1;
1001
1002   dx = sdx * dx + 1;
1003   dy = sdy * dy + 1;
1004
1005   x = y = 0;
1006
1007   px = x1;
1008   py = y1;
1009
1010   if (dx >= dy)
1011   {
1012     for (x = 0; x < dx; x++)
1013     {
1014       Callback(Surface, px, py, Color);
1015
1016       y += dy;
1017       if (y >= dx)
1018       {
1019         y -= dx;
1020         py += sdy;
1021       }
1022
1023       px += sdx;
1024     }
1025   }
1026   else
1027   {
1028     for (y = 0; y < dy; y++)
1029     {
1030       Callback(Surface, px, py, Color);
1031
1032       x += dx;
1033       if (x >= dy)
1034       {
1035         x -= dy;
1036         px += sdx;
1037       }
1038
1039       py += sdy;
1040     }
1041   }
1042 }
1043
1044 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1045                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1046                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1047                                  Uint32 Color))
1048 {
1049   sge_DoLine(Surface, X1, Y1, X2, Y2,
1050              SDL_MapRGB(Surface->format, R, G, B), Callback);
1051 }
1052
1053 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1054               Uint32 Color)
1055 {
1056   if (SDL_MUSTLOCK(Surface))
1057   {
1058     if (SDL_LockSurface(Surface) < 0)
1059       return;
1060    }
1061
1062    /* Draw the line */
1063    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1064
1065    /* unlock the display */
1066    if (SDL_MUSTLOCK(Surface))
1067    {
1068       SDL_UnlockSurface(Surface);
1069    }
1070 }
1071
1072 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1073                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1074 {
1075   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1076 }
1077
1078 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1079 {
1080   if (dst_bitmap == backbuffer || dst_bitmap == window)
1081   {
1082     x += video_xoffset;
1083     y += video_yoffset;
1084   }
1085
1086   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1087 }
1088
1089
1090 /*
1091   -----------------------------------------------------------------------------
1092   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1093   -----------------------------------------------------------------------------
1094 */
1095
1096 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1097                    int width, int height, Uint32 color)
1098 {
1099   int x, y;
1100
1101   for (y = src_y; y < src_y + height; y++)
1102   {
1103     for (x = src_x; x < src_x + width; x++)
1104     {
1105       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1106
1107       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1108     }
1109   }
1110 }
1111
1112 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1113                           int src_x, int src_y, int width, int height,
1114                           int dst_x, int dst_y)
1115 {
1116   int x, y;
1117
1118   for (y = 0; y < height; y++)
1119   {
1120     for (x = 0; x < width; x++)
1121     {
1122       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1123
1124       if (pixel != BLACK_PIXEL)
1125         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1126     }
1127   }
1128 }
1129
1130
1131 /* ========================================================================= */
1132 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1133 /* (Rotozoomer) by Andreas Schiffler                                         */
1134 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1135 /* ========================================================================= */
1136
1137 /*
1138   -----------------------------------------------------------------------------
1139   32 bit zoomer
1140
1141   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1142   -----------------------------------------------------------------------------
1143 */
1144
1145 typedef struct
1146 {
1147   Uint8 r;
1148   Uint8 g;
1149   Uint8 b;
1150   Uint8 a;
1151 } tColorRGBA;
1152
1153 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1154 {
1155   int x, y;
1156   tColorRGBA *sp, *csp, *dp;
1157   int sgap, dgap;
1158
1159   /* pointer setup */
1160   sp = csp = (tColorRGBA *) src->pixels;
1161   dp = (tColorRGBA *) dst->pixels;
1162   sgap = src->pitch - src->w * 4;
1163   dgap = dst->pitch - dst->w * 4;
1164
1165   for (y = 0; y < dst->h; y++)
1166   {
1167     sp = csp;
1168
1169     for (x = 0; x < dst->w; x++)
1170     {
1171       tColorRGBA *sp0 = sp;
1172       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1173       tColorRGBA *sp00 = &sp0[0];
1174       tColorRGBA *sp01 = &sp0[1];
1175       tColorRGBA *sp10 = &sp1[0];
1176       tColorRGBA *sp11 = &sp1[1];
1177       tColorRGBA new;
1178
1179       /* create new color pixel from all four source color pixels */
1180       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1181       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1182       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1183       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1184
1185       /* draw */
1186       *dp = new;
1187
1188       /* advance source pointers */
1189       sp += 2;
1190
1191       /* advance destination pointer */
1192       dp++;
1193     }
1194
1195     /* advance source pointer */
1196     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1197
1198     /* advance destination pointers */
1199     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1200   }
1201
1202   return 0;
1203 }
1204
1205 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1206 {
1207   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1208   tColorRGBA *sp, *csp, *dp;
1209   int sgap, dgap;
1210
1211   /* use specialized zoom function when scaling down to exactly half size */
1212   if (src->w == 2 * dst->w &&
1213       src->h == 2 * dst->h)
1214     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1215
1216   /* variable setup */
1217   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1218   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1219
1220   /* allocate memory for row increments */
1221   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1222   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1223
1224   /* precalculate row increments */
1225   csx = 0;
1226   csax = sax;
1227   for (x = 0; x <= dst->w; x++)
1228   {
1229     *csax = csx;
1230     csax++;
1231     csx &= 0xffff;
1232     csx += sx;
1233   }
1234
1235   csy = 0;
1236   csay = say;
1237   for (y = 0; y <= dst->h; y++)
1238   {
1239     *csay = csy;
1240     csay++;
1241     csy &= 0xffff;
1242     csy += sy;
1243   }
1244
1245   /* pointer setup */
1246   sp = csp = (tColorRGBA *) src->pixels;
1247   dp = (tColorRGBA *) dst->pixels;
1248   sgap = src->pitch - src->w * 4;
1249   dgap = dst->pitch - dst->w * 4;
1250
1251   csay = say;
1252   for (y = 0; y < dst->h; y++)
1253   {
1254     sp = csp;
1255     csax = sax;
1256
1257     for (x = 0; x < dst->w; x++)
1258     {
1259       /* draw */
1260       *dp = *sp;
1261
1262       /* advance source pointers */
1263       csax++;
1264       sp += (*csax >> 16);
1265
1266       /* advance destination pointer */
1267       dp++;
1268     }
1269
1270     /* advance source pointer */
1271     csay++;
1272     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1273
1274     /* advance destination pointers */
1275     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1276   }
1277
1278   free(sax);
1279   free(say);
1280
1281   return 0;
1282 }
1283
1284 /*
1285   -----------------------------------------------------------------------------
1286   8 bit zoomer
1287
1288   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1289   -----------------------------------------------------------------------------
1290 */
1291
1292 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1293 {
1294   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1295   Uint8 *sp, *dp, *csp;
1296   int dgap;
1297
1298   /* variable setup */
1299   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1300   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1301
1302   /* allocate memory for row increments */
1303   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1304   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1305
1306   /* precalculate row increments */
1307   csx = 0;
1308   csax = sax;
1309   for (x = 0; x < dst->w; x++)
1310   {
1311     csx += sx;
1312     *csax = (csx >> 16);
1313     csx &= 0xffff;
1314     csax++;
1315   }
1316
1317   csy = 0;
1318   csay = say;
1319   for (y = 0; y < dst->h; y++)
1320   {
1321     csy += sy;
1322     *csay = (csy >> 16);
1323     csy &= 0xffff;
1324     csay++;
1325   }
1326
1327   csx = 0;
1328   csax = sax;
1329   for (x = 0; x < dst->w; x++)
1330   {
1331     csx += (*csax);
1332     csax++;
1333   }
1334
1335   csy = 0;
1336   csay = say;
1337   for (y = 0; y < dst->h; y++)
1338   {
1339     csy += (*csay);
1340     csay++;
1341   }
1342
1343   /* pointer setup */
1344   sp = csp = (Uint8 *) src->pixels;
1345   dp = (Uint8 *) dst->pixels;
1346   dgap = dst->pitch - dst->w;
1347
1348   /* draw */
1349   csay = say;
1350   for (y = 0; y < dst->h; y++)
1351   {
1352     csax = sax;
1353     sp = csp;
1354     for (x = 0; x < dst->w; x++)
1355     {
1356       /* draw */
1357       *dp = *sp;
1358
1359       /* advance source pointers */
1360       sp += (*csax);
1361       csax++;
1362
1363       /* advance destination pointer */
1364       dp++;
1365     }
1366
1367     /* advance source pointer (for row) */
1368     csp += ((*csay) * src->pitch);
1369     csay++;
1370
1371     /* advance destination pointers */
1372     dp += dgap;
1373   }
1374
1375   free(sax);
1376   free(say);
1377
1378   return 0;
1379 }
1380
1381 /*
1382   -----------------------------------------------------------------------------
1383   zoomSurface()
1384
1385   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1386   'zoomx' and 'zoomy' are scaling factors for width and height.
1387   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1388   into a 32bit RGBA format on the fly.
1389   -----------------------------------------------------------------------------
1390 */
1391
1392 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1393 {
1394   SDL_Surface *zoom_src = NULL;
1395   SDL_Surface *zoom_dst = NULL;
1396   boolean is_converted = FALSE;
1397   boolean is_32bit;
1398   int i;
1399
1400   if (src == NULL)
1401     return NULL;
1402
1403   /* determine if source surface is 32 bit or 8 bit */
1404   is_32bit = (src->format->BitsPerPixel == 32);
1405
1406   if (is_32bit || src->format->BitsPerPixel == 8)
1407   {
1408     /* use source surface 'as is' */
1409     zoom_src = src;
1410   }
1411   else
1412   {
1413     /* new source surface is 32 bit with a defined RGB ordering */
1414     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1415                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1416     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1417     is_32bit = TRUE;
1418     is_converted = TRUE;
1419   }
1420
1421   /* allocate surface to completely contain the zoomed surface */
1422   if (is_32bit)
1423   {
1424     /* target surface is 32 bit with source RGBA/ABGR ordering */
1425     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1426                                     zoom_src->format->Rmask,
1427                                     zoom_src->format->Gmask,
1428                                     zoom_src->format->Bmask, 0);
1429   }
1430   else
1431   {
1432     /* target surface is 8 bit */
1433     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1434                                     0, 0, 0, 0);
1435   }
1436
1437   /* lock source surface */
1438   SDL_LockSurface(zoom_src);
1439
1440   /* check which kind of surface we have */
1441   if (is_32bit)
1442   {
1443     /* call the 32 bit transformation routine to do the zooming */
1444     zoomSurfaceRGBA(zoom_src, zoom_dst);
1445   }
1446   else
1447   {
1448     /* copy palette */
1449     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1450       zoom_dst->format->palette->colors[i] =
1451         zoom_src->format->palette->colors[i];
1452     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1453
1454     /* call the 8 bit transformation routine to do the zooming */
1455     zoomSurfaceY(zoom_src, zoom_dst);
1456   }
1457
1458   /* unlock source surface */
1459   SDL_UnlockSurface(zoom_src);
1460
1461   /* free temporary surface */
1462   if (is_converted)
1463     SDL_FreeSurface(zoom_src);
1464
1465   /* return destination surface */
1466   return zoom_dst;
1467 }
1468
1469 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1470 {
1471   SDL_Surface *sdl_surface_tmp;
1472   int dst_width = dst_bitmap->width;
1473   int dst_height = dst_bitmap->height;
1474
1475   /* throw away old destination surface */
1476   SDL_FreeSurface(dst_bitmap->surface);
1477
1478   /* create zoomed temporary surface from source surface */
1479   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1480
1481   /* create native format destination surface from zoomed temporary surface */
1482   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1483
1484   /* free temporary surface */
1485   SDL_FreeSurface(sdl_surface_tmp);
1486 }
1487
1488
1489 /* ========================================================================= */
1490 /* load image to bitmap                                                      */
1491 /* ========================================================================= */
1492
1493 Bitmap *SDLLoadImage(char *filename)
1494 {
1495   Bitmap *new_bitmap = CreateBitmapStruct();
1496   SDL_Surface *sdl_image_tmp;
1497
1498   /* load image to temporary surface */
1499   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1500   {
1501     SetError("IMG_Load(): %s", SDL_GetError());
1502
1503     return NULL;
1504   }
1505
1506   /* create native non-transparent surface for current image */
1507   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1508   {
1509     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1510
1511     return NULL;
1512   }
1513
1514   /* create native transparent surface for current image */
1515   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1516                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1517   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1518   {
1519     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1520
1521     return NULL;
1522   }
1523
1524   /* free temporary surface */
1525   SDL_FreeSurface(sdl_image_tmp);
1526
1527   new_bitmap->width = new_bitmap->surface->w;
1528   new_bitmap->height = new_bitmap->surface->h;
1529
1530   return new_bitmap;
1531 }
1532
1533
1534 /* ------------------------------------------------------------------------- */
1535 /* custom cursor fuctions                                                    */
1536 /* ------------------------------------------------------------------------- */
1537
1538 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1539 {
1540   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1541                           cursor_info->width, cursor_info->height,
1542                           cursor_info->hot_x, cursor_info->hot_y);
1543 }
1544
1545 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1546 {
1547   static struct MouseCursorInfo *last_cursor_info = NULL;
1548   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1549   static SDL_Cursor *cursor_default = NULL;
1550   static SDL_Cursor *cursor_current = NULL;
1551
1552   /* if invoked for the first time, store the SDL default cursor */
1553   if (cursor_default == NULL)
1554     cursor_default = SDL_GetCursor();
1555
1556   /* only create new cursor if cursor info (custom only) has changed */
1557   if (cursor_info != NULL && cursor_info != last_cursor_info)
1558   {
1559     cursor_current = create_cursor(cursor_info);
1560     last_cursor_info = cursor_info;
1561   }
1562
1563   /* only set new cursor if cursor info (custom or NULL) has changed */
1564   if (cursor_info != last_cursor_info2)
1565     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1566
1567   last_cursor_info2 = cursor_info;
1568 }
1569
1570
1571 /* ========================================================================= */
1572 /* audio functions                                                           */
1573 /* ========================================================================= */
1574
1575 void SDLOpenAudio(void)
1576 {
1577   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1578     putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1579
1580   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1581   {
1582     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1583     return;
1584   }
1585
1586   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1587                     AUDIO_NUM_CHANNELS_STEREO,
1588                     setup.system.audio_fragment_size) < 0)
1589   {
1590     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1591     return;
1592   }
1593
1594   audio.sound_available = TRUE;
1595   audio.music_available = TRUE;
1596   audio.loops_available = TRUE;
1597   audio.sound_enabled = TRUE;
1598
1599   /* set number of available mixer channels */
1600   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1601   audio.music_channel = MUSIC_CHANNEL;
1602   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1603
1604   Mixer_InitChannels();
1605 }
1606
1607 void SDLCloseAudio(void)
1608 {
1609   Mix_HaltMusic();
1610   Mix_HaltChannel(-1);
1611
1612   Mix_CloseAudio();
1613   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1614 }
1615
1616
1617 /* ========================================================================= */
1618 /* event functions                                                           */
1619 /* ========================================================================= */
1620
1621 void SDLNextEvent(Event *event)
1622 {
1623   SDL_WaitEvent(event);
1624
1625   if (event->type == EVENT_BUTTONPRESS ||
1626       event->type == EVENT_BUTTONRELEASE)
1627   {
1628     if (((ButtonEvent *)event)->x > video_xoffset)
1629       ((ButtonEvent *)event)->x -= video_xoffset;
1630     else
1631       ((ButtonEvent *)event)->x = 0;
1632     if (((ButtonEvent *)event)->y > video_yoffset)
1633       ((ButtonEvent *)event)->y -= video_yoffset;
1634     else
1635       ((ButtonEvent *)event)->y = 0;
1636   }
1637   else if (event->type == EVENT_MOTIONNOTIFY)
1638   {
1639     if (((MotionEvent *)event)->x > video_xoffset)
1640       ((MotionEvent *)event)->x -= video_xoffset;
1641     else
1642       ((MotionEvent *)event)->x = 0;
1643     if (((MotionEvent *)event)->y > video_yoffset)
1644       ((MotionEvent *)event)->y -= video_yoffset;
1645     else
1646       ((MotionEvent *)event)->y = 0;
1647   }
1648 }
1649
1650
1651 /* ========================================================================= */
1652 /* joystick functions                                                        */
1653 /* ========================================================================= */
1654
1655 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1656 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1657 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1658
1659 static boolean SDLOpenJoystick(int nr)
1660 {
1661   if (nr < 0 || nr > MAX_PLAYERS)
1662     return FALSE;
1663
1664   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1665 }
1666
1667 static void SDLCloseJoystick(int nr)
1668 {
1669   if (nr < 0 || nr > MAX_PLAYERS)
1670     return;
1671
1672   SDL_JoystickClose(sdl_joystick[nr]);
1673 }
1674
1675 static boolean SDLCheckJoystickOpened(int nr)
1676 {
1677   if (nr < 0 || nr > MAX_PLAYERS)
1678     return FALSE;
1679
1680   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1681 }
1682
1683 void HandleJoystickEvent(Event *event)
1684 {
1685   switch(event->type)
1686   {
1687     case SDL_JOYAXISMOTION:
1688       if (event->jaxis.axis < 2)
1689         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1690       break;
1691
1692     case SDL_JOYBUTTONDOWN:
1693       if (event->jbutton.button < 2)
1694         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1695       break;
1696
1697     case SDL_JOYBUTTONUP:
1698       if (event->jbutton.button < 2)
1699         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1700       break;
1701
1702     default:
1703       break;
1704   }
1705 }
1706
1707 void SDLInitJoysticks()
1708 {
1709   static boolean sdl_joystick_subsystem_initialized = FALSE;
1710   boolean print_warning = !sdl_joystick_subsystem_initialized;
1711   int i;
1712
1713   if (!sdl_joystick_subsystem_initialized)
1714   {
1715     sdl_joystick_subsystem_initialized = TRUE;
1716
1717     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1718     {
1719       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1720       return;
1721     }
1722   }
1723
1724   for (i = 0; i < MAX_PLAYERS; i++)
1725   {
1726     /* get configured joystick for this player */
1727     char *device_name = setup.input[i].joy.device_name;
1728     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1729
1730     if (joystick_nr >= SDL_NumJoysticks())
1731     {
1732       if (setup.input[i].use_joystick && print_warning)
1733         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1734
1735       joystick_nr = -1;
1736     }
1737
1738     /* misuse joystick file descriptor variable to store joystick number */
1739     joystick.fd[i] = joystick_nr;
1740
1741     if (joystick_nr == -1)
1742       continue;
1743
1744     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1745     if (SDLCheckJoystickOpened(joystick_nr))
1746       SDLCloseJoystick(joystick_nr);
1747
1748     if (!setup.input[i].use_joystick)
1749       continue;
1750
1751     if (!SDLOpenJoystick(joystick_nr))
1752     {
1753       if (print_warning)
1754         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1755
1756       continue;
1757     }
1758
1759     joystick.status = JOYSTICK_ACTIVATED;
1760   }
1761 }
1762
1763 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1764 {
1765   if (nr < 0 || nr >= MAX_PLAYERS)
1766     return FALSE;
1767
1768   if (x != NULL)
1769     *x = sdl_js_axis[nr][0];
1770   if (y != NULL)
1771     *y = sdl_js_axis[nr][1];
1772
1773   if (b1 != NULL)
1774     *b1 = sdl_js_button[nr][0];
1775   if (b2 != NULL)
1776     *b2 = sdl_js_button[nr][1];
1777
1778   return TRUE;
1779 }
1780
1781 #endif /* TARGET_SDL */