rnd-20130916-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 #if 0
1338   int sgap;
1339 #endif
1340   int dgap;
1341
1342   /* pointer setup */
1343   sp = csp = (tColorRGBA *) src->pixels;
1344   dp = (tColorRGBA *) dst->pixels;
1345 #if 0
1346   sgap = src->pitch - src->w * 4;
1347 #endif
1348   dgap = dst->pitch - dst->w * 4;
1349
1350   for (y = 0; y < dst->h; y++)
1351   {
1352     sp = csp;
1353
1354     for (x = 0; x < dst->w; x++)
1355     {
1356       tColorRGBA *sp0 = sp;
1357       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1358       tColorRGBA *sp00 = &sp0[0];
1359       tColorRGBA *sp01 = &sp0[1];
1360       tColorRGBA *sp10 = &sp1[0];
1361       tColorRGBA *sp11 = &sp1[1];
1362       tColorRGBA new;
1363
1364       /* create new color pixel from all four source color pixels */
1365       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1366       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1367       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1368       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1369
1370       /* draw */
1371       *dp = new;
1372
1373       /* advance source pointers */
1374       sp += 2;
1375
1376       /* advance destination pointer */
1377       dp++;
1378     }
1379
1380     /* advance source pointer */
1381     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1382
1383     /* advance destination pointers */
1384     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1385   }
1386
1387   return 0;
1388 }
1389
1390 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1391 {
1392   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1393   tColorRGBA *sp, *csp, *dp;
1394 #if 0
1395   int sgap;
1396 #endif
1397   int dgap;
1398
1399   /* use specialized zoom function when scaling down to exactly half size */
1400   if (src->w == 2 * dst->w &&
1401       src->h == 2 * dst->h)
1402     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1403
1404   /* variable setup */
1405   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1406   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1407
1408   /* allocate memory for row increments */
1409   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1410   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1411
1412   /* precalculate row increments */
1413   csx = 0;
1414   csax = sax;
1415   for (x = 0; x <= dst->w; x++)
1416   {
1417     *csax = csx;
1418     csax++;
1419     csx &= 0xffff;
1420     csx += sx;
1421   }
1422
1423   csy = 0;
1424   csay = say;
1425   for (y = 0; y <= dst->h; y++)
1426   {
1427     *csay = csy;
1428     csay++;
1429     csy &= 0xffff;
1430     csy += sy;
1431   }
1432
1433   /* pointer setup */
1434   sp = csp = (tColorRGBA *) src->pixels;
1435   dp = (tColorRGBA *) dst->pixels;
1436 #if 0
1437   sgap = src->pitch - src->w * 4;
1438 #endif
1439   dgap = dst->pitch - dst->w * 4;
1440
1441   csay = say;
1442   for (y = 0; y < dst->h; y++)
1443   {
1444     sp = csp;
1445     csax = sax;
1446
1447     for (x = 0; x < dst->w; x++)
1448     {
1449       /* draw */
1450       *dp = *sp;
1451
1452       /* advance source pointers */
1453       csax++;
1454       sp += (*csax >> 16);
1455
1456       /* advance destination pointer */
1457       dp++;
1458     }
1459
1460     /* advance source pointer */
1461     csay++;
1462     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1463
1464     /* advance destination pointers */
1465     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1466   }
1467
1468   free(sax);
1469   free(say);
1470
1471   return 0;
1472 }
1473
1474 /*
1475   -----------------------------------------------------------------------------
1476   8 bit zoomer
1477
1478   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1479   -----------------------------------------------------------------------------
1480 */
1481
1482 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1483 {
1484   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1485   Uint8 *sp, *dp, *csp;
1486   int dgap;
1487
1488   /* variable setup */
1489   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1490   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1491
1492   /* allocate memory for row increments */
1493   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1494   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1495
1496   /* precalculate row increments */
1497   csx = 0;
1498   csax = sax;
1499   for (x = 0; x < dst->w; x++)
1500   {
1501     csx += sx;
1502     *csax = (csx >> 16);
1503     csx &= 0xffff;
1504     csax++;
1505   }
1506
1507   csy = 0;
1508   csay = say;
1509   for (y = 0; y < dst->h; y++)
1510   {
1511     csy += sy;
1512     *csay = (csy >> 16);
1513     csy &= 0xffff;
1514     csay++;
1515   }
1516
1517   csx = 0;
1518   csax = sax;
1519   for (x = 0; x < dst->w; x++)
1520   {
1521     csx += (*csax);
1522     csax++;
1523   }
1524
1525   csy = 0;
1526   csay = say;
1527   for (y = 0; y < dst->h; y++)
1528   {
1529     csy += (*csay);
1530     csay++;
1531   }
1532
1533   /* pointer setup */
1534   sp = csp = (Uint8 *) src->pixels;
1535   dp = (Uint8 *) dst->pixels;
1536   dgap = dst->pitch - dst->w;
1537
1538   /* draw */
1539   csay = say;
1540   for (y = 0; y < dst->h; y++)
1541   {
1542     csax = sax;
1543     sp = csp;
1544     for (x = 0; x < dst->w; x++)
1545     {
1546       /* draw */
1547       *dp = *sp;
1548
1549       /* advance source pointers */
1550       sp += (*csax);
1551       csax++;
1552
1553       /* advance destination pointer */
1554       dp++;
1555     }
1556
1557     /* advance source pointer (for row) */
1558     csp += ((*csay) * src->pitch);
1559     csay++;
1560
1561     /* advance destination pointers */
1562     dp += dgap;
1563   }
1564
1565   free(sax);
1566   free(say);
1567
1568   return 0;
1569 }
1570
1571 /*
1572   -----------------------------------------------------------------------------
1573   zoomSurface()
1574
1575   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1576   'zoomx' and 'zoomy' are scaling factors for width and height.
1577   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1578   into a 32bit RGBA format on the fly.
1579   -----------------------------------------------------------------------------
1580 */
1581
1582 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1583 {
1584   SDL_Surface *zoom_src = NULL;
1585   SDL_Surface *zoom_dst = NULL;
1586   boolean is_converted = FALSE;
1587   boolean is_32bit;
1588   int i;
1589
1590   if (src == NULL)
1591     return NULL;
1592
1593   /* determine if source surface is 32 bit or 8 bit */
1594   is_32bit = (src->format->BitsPerPixel == 32);
1595
1596   if (is_32bit || src->format->BitsPerPixel == 8)
1597   {
1598     /* use source surface 'as is' */
1599     zoom_src = src;
1600   }
1601   else
1602   {
1603     /* new source surface is 32 bit with a defined RGB ordering */
1604     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1605                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1606     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1607     is_32bit = TRUE;
1608     is_converted = TRUE;
1609   }
1610
1611   /* allocate surface to completely contain the zoomed surface */
1612   if (is_32bit)
1613   {
1614     /* target surface is 32 bit with source RGBA/ABGR ordering */
1615     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1616                                     zoom_src->format->Rmask,
1617                                     zoom_src->format->Gmask,
1618                                     zoom_src->format->Bmask, 0);
1619   }
1620   else
1621   {
1622     /* target surface is 8 bit */
1623     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1624                                     0, 0, 0, 0);
1625   }
1626
1627   /* lock source surface */
1628   SDL_LockSurface(zoom_src);
1629
1630   /* check which kind of surface we have */
1631   if (is_32bit)
1632   {
1633     /* call the 32 bit transformation routine to do the zooming */
1634     zoomSurfaceRGBA(zoom_src, zoom_dst);
1635   }
1636   else
1637   {
1638     /* copy palette */
1639     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1640       zoom_dst->format->palette->colors[i] =
1641         zoom_src->format->palette->colors[i];
1642     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1643
1644     /* call the 8 bit transformation routine to do the zooming */
1645     zoomSurfaceY(zoom_src, zoom_dst);
1646   }
1647
1648   /* unlock source surface */
1649   SDL_UnlockSurface(zoom_src);
1650
1651   /* free temporary surface */
1652   if (is_converted)
1653     SDL_FreeSurface(zoom_src);
1654
1655   /* return destination surface */
1656   return zoom_dst;
1657 }
1658
1659 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1660 {
1661   SDL_Surface *sdl_surface_tmp;
1662   int dst_width = dst_bitmap->width;
1663   int dst_height = dst_bitmap->height;
1664
1665   /* throw away old destination surface */
1666   SDL_FreeSurface(dst_bitmap->surface);
1667
1668   /* create zoomed temporary surface from source surface */
1669   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1670
1671   /* create native format destination surface from zoomed temporary surface */
1672   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1673
1674   /* free temporary surface */
1675   SDL_FreeSurface(sdl_surface_tmp);
1676 }
1677
1678
1679 /* ========================================================================= */
1680 /* load image to bitmap                                                      */
1681 /* ========================================================================= */
1682
1683 Bitmap *SDLLoadImage(char *filename)
1684 {
1685   Bitmap *new_bitmap = CreateBitmapStruct();
1686   SDL_Surface *sdl_image_tmp;
1687
1688   /* load image to temporary surface */
1689   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1690   {
1691     SetError("IMG_Load(): %s", SDL_GetError());
1692
1693     return NULL;
1694   }
1695
1696   UPDATE_BUSY_STATE();
1697
1698   /* create native non-transparent surface for current image */
1699   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1700   {
1701     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1702
1703     return NULL;
1704   }
1705
1706   UPDATE_BUSY_STATE();
1707
1708   /* create native transparent surface for current image */
1709   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1710                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1711   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1712   {
1713     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1714
1715     return NULL;
1716   }
1717
1718   UPDATE_BUSY_STATE();
1719
1720   /* free temporary surface */
1721   SDL_FreeSurface(sdl_image_tmp);
1722
1723   new_bitmap->width = new_bitmap->surface->w;
1724   new_bitmap->height = new_bitmap->surface->h;
1725
1726   return new_bitmap;
1727 }
1728
1729
1730 /* ------------------------------------------------------------------------- */
1731 /* custom cursor fuctions                                                    */
1732 /* ------------------------------------------------------------------------- */
1733
1734 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1735 {
1736   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1737                           cursor_info->width, cursor_info->height,
1738                           cursor_info->hot_x, cursor_info->hot_y);
1739 }
1740
1741 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1742 {
1743   static struct MouseCursorInfo *last_cursor_info = NULL;
1744   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1745   static SDL_Cursor *cursor_default = NULL;
1746   static SDL_Cursor *cursor_current = NULL;
1747
1748   /* if invoked for the first time, store the SDL default cursor */
1749   if (cursor_default == NULL)
1750     cursor_default = SDL_GetCursor();
1751
1752   /* only create new cursor if cursor info (custom only) has changed */
1753   if (cursor_info != NULL && cursor_info != last_cursor_info)
1754   {
1755     cursor_current = create_cursor(cursor_info);
1756     last_cursor_info = cursor_info;
1757   }
1758
1759   /* only set new cursor if cursor info (custom or NULL) has changed */
1760   if (cursor_info != last_cursor_info2)
1761     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1762
1763   last_cursor_info2 = cursor_info;
1764 }
1765
1766
1767 /* ========================================================================= */
1768 /* audio functions                                                           */
1769 /* ========================================================================= */
1770
1771 void SDLOpenAudio(void)
1772 {
1773   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1774     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1775
1776   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1777   {
1778     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1779     return;
1780   }
1781
1782   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1783                     AUDIO_NUM_CHANNELS_STEREO,
1784                     setup.system.audio_fragment_size) < 0)
1785   {
1786     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1787     return;
1788   }
1789
1790   audio.sound_available = TRUE;
1791   audio.music_available = TRUE;
1792   audio.loops_available = TRUE;
1793   audio.sound_enabled = TRUE;
1794
1795   /* set number of available mixer channels */
1796   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1797   audio.music_channel = MUSIC_CHANNEL;
1798   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1799
1800   Mixer_InitChannels();
1801 }
1802
1803 void SDLCloseAudio(void)
1804 {
1805   Mix_HaltMusic();
1806   Mix_HaltChannel(-1);
1807
1808   Mix_CloseAudio();
1809   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1810 }
1811
1812
1813 /* ========================================================================= */
1814 /* event functions                                                           */
1815 /* ========================================================================= */
1816
1817 void SDLNextEvent(Event *event)
1818 {
1819   SDL_WaitEvent(event);
1820
1821   if (event->type == EVENT_BUTTONPRESS ||
1822       event->type == EVENT_BUTTONRELEASE)
1823   {
1824     if (((ButtonEvent *)event)->x > video_xoffset)
1825       ((ButtonEvent *)event)->x -= video_xoffset;
1826     else
1827       ((ButtonEvent *)event)->x = 0;
1828     if (((ButtonEvent *)event)->y > video_yoffset)
1829       ((ButtonEvent *)event)->y -= video_yoffset;
1830     else
1831       ((ButtonEvent *)event)->y = 0;
1832   }
1833   else if (event->type == EVENT_MOTIONNOTIFY)
1834   {
1835     if (((MotionEvent *)event)->x > video_xoffset)
1836       ((MotionEvent *)event)->x -= video_xoffset;
1837     else
1838       ((MotionEvent *)event)->x = 0;
1839     if (((MotionEvent *)event)->y > video_yoffset)
1840       ((MotionEvent *)event)->y -= video_yoffset;
1841     else
1842       ((MotionEvent *)event)->y = 0;
1843   }
1844 }
1845
1846 void SDLHandleWindowManagerEvent(Event *event)
1847 {
1848 #if defined(PLATFORM_WIN32)
1849   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
1850   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
1851
1852   if (syswmmsg->msg == WM_DROPFILES)
1853   {
1854     HDROP hdrop = (HDROP)syswmmsg->wParam;
1855     int i, num_files;
1856
1857     printf("::: SDL_SYSWMEVENT:\n");
1858
1859     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
1860
1861     for (i = 0; i < num_files; i++)
1862     {
1863       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
1864       char buffer[buffer_len + 1];
1865
1866       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
1867
1868       printf("::: - '%s'\n", buffer);
1869     }
1870
1871     DragFinish((HDROP)syswmmsg->wParam);
1872   }
1873 #endif
1874 }
1875
1876
1877 /* ========================================================================= */
1878 /* joystick functions                                                        */
1879 /* ========================================================================= */
1880
1881 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1882 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1883 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1884
1885 static boolean SDLOpenJoystick(int nr)
1886 {
1887   if (nr < 0 || nr > MAX_PLAYERS)
1888     return FALSE;
1889
1890   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1891 }
1892
1893 static void SDLCloseJoystick(int nr)
1894 {
1895   if (nr < 0 || nr > MAX_PLAYERS)
1896     return;
1897
1898   SDL_JoystickClose(sdl_joystick[nr]);
1899 }
1900
1901 static boolean SDLCheckJoystickOpened(int nr)
1902 {
1903   if (nr < 0 || nr > MAX_PLAYERS)
1904     return FALSE;
1905
1906   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1907 }
1908
1909 void HandleJoystickEvent(Event *event)
1910 {
1911   switch(event->type)
1912   {
1913     case SDL_JOYAXISMOTION:
1914       if (event->jaxis.axis < 2)
1915         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1916       break;
1917
1918     case SDL_JOYBUTTONDOWN:
1919       if (event->jbutton.button < 2)
1920         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1921       break;
1922
1923     case SDL_JOYBUTTONUP:
1924       if (event->jbutton.button < 2)
1925         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1926       break;
1927
1928     default:
1929       break;
1930   }
1931 }
1932
1933 void SDLInitJoysticks()
1934 {
1935   static boolean sdl_joystick_subsystem_initialized = FALSE;
1936   boolean print_warning = !sdl_joystick_subsystem_initialized;
1937   int i;
1938
1939   if (!sdl_joystick_subsystem_initialized)
1940   {
1941     sdl_joystick_subsystem_initialized = TRUE;
1942
1943     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1944     {
1945       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1946       return;
1947     }
1948   }
1949
1950   for (i = 0; i < MAX_PLAYERS; i++)
1951   {
1952     /* get configured joystick for this player */
1953     char *device_name = setup.input[i].joy.device_name;
1954     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1955
1956     if (joystick_nr >= SDL_NumJoysticks())
1957     {
1958       if (setup.input[i].use_joystick && print_warning)
1959         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1960
1961       joystick_nr = -1;
1962     }
1963
1964     /* misuse joystick file descriptor variable to store joystick number */
1965     joystick.fd[i] = joystick_nr;
1966
1967     if (joystick_nr == -1)
1968       continue;
1969
1970     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1971     if (SDLCheckJoystickOpened(joystick_nr))
1972       SDLCloseJoystick(joystick_nr);
1973
1974     if (!setup.input[i].use_joystick)
1975       continue;
1976
1977     if (!SDLOpenJoystick(joystick_nr))
1978     {
1979       if (print_warning)
1980         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1981
1982       continue;
1983     }
1984
1985     joystick.status = JOYSTICK_ACTIVATED;
1986   }
1987 }
1988
1989 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1990 {
1991   if (nr < 0 || nr >= MAX_PLAYERS)
1992     return FALSE;
1993
1994   if (x != NULL)
1995     *x = sdl_js_axis[nr][0];
1996   if (y != NULL)
1997     *y = sdl_js_axis[nr][1];
1998
1999   if (b1 != NULL)
2000     *b1 = sdl_js_button[nr][0];
2001   if (b2 != NULL)
2002     *b2 = sdl_js_button[nr][1];
2003
2004   return TRUE;
2005 }
2006
2007 #endif /* TARGET_SDL */