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