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