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