added test code for using bitmaps instead of textures for global animations
[rocksndiamonds.git] / src / libgame / sdl.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // sdl.c
10 // ============================================================================
11
12 #include "system.h"
13 #include "sound.h"
14 #include "joystick.h"
15 #include "misc.h"
16 #include "setup.h"
17
18 #define ENABLE_UNUSED_CODE      0       /* currently unused functions */
19
20
21 /* ========================================================================= */
22 /* video functions                                                           */
23 /* ========================================================================= */
24
25 /* SDL internal variables */
26 #if defined(TARGET_SDL2)
27 static SDL_Window *sdl_window = NULL;
28 static SDL_Renderer *sdl_renderer = NULL;
29 static SDL_Texture *sdl_texture = NULL;
30 static boolean fullscreen_enabled = FALSE;
31
32 #define USE_RENDERER    TRUE
33 #endif
34
35 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
36 static int fullscreen_width;
37 static int fullscreen_height;
38 static int fullscreen_xoffset;
39 static int fullscreen_yoffset;
40 static int video_xoffset;
41 static int video_yoffset;
42 static boolean limit_screen_updates = FALSE;
43
44
45 /* functions from SGE library */
46 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
47
48 void SDLLimitScreenUpdates(boolean enable)
49 {
50   limit_screen_updates = enable;
51 }
52
53 static void UpdateScreen(SDL_Rect *rect)
54 {
55   static unsigned int update_screen_delay = 0;
56   unsigned int update_screen_delay_value = 20;          /* (milliseconds) */
57   SDL_Surface *screen = backbuffer->surface;
58
59   if (limit_screen_updates &&
60       !DelayReached(&update_screen_delay, update_screen_delay_value))
61     return;
62
63   LimitScreenUpdates(FALSE);
64
65 #if 0
66   {
67     static int LastFrameCounter = 0;
68     boolean changed = (FrameCounter != LastFrameCounter);
69
70     printf("::: FrameCounter == %d [%s]\n", FrameCounter,
71            (changed ? "-" : "SAME FRAME UPDATED"));
72
73     LastFrameCounter = FrameCounter;
74
75     /*
76     if (FrameCounter % 2)
77       return;
78     */
79   }
80 #endif
81
82 #if USE_FINAL_SCREEN_BITMAP
83   if (gfx.final_screen_bitmap != NULL)  // may not be initialized yet
84   {
85     // !!! TEST !!!
86     // draw global animations using bitmaps instead of using textures
87     // to prevent texture scaling artefacts (this is potentially slower)
88
89     BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
90                gfx.win_xsize, gfx.win_ysize, 0, 0);
91
92     // copy global animations to render target buffer, if defined
93     if (gfx.draw_global_anim_function != NULL)
94       gfx.draw_global_anim_function();
95
96     screen = gfx.final_screen_bitmap->surface;
97
98     // force full window redraw
99     rect = NULL;
100   }
101 #endif
102
103 #if defined(TARGET_SDL2)
104 #if USE_RENDERER
105   if (rect)
106   {
107     int bytes_x = screen->pitch / video.width;
108     int bytes_y = screen->pitch;
109
110     if (video.fullscreen_enabled)
111       bytes_x = screen->pitch / fullscreen_width;
112
113     SDL_UpdateTexture(sdl_texture, rect,
114                       screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
115                       screen->pitch);
116   }
117   else
118   {
119     SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
120   }
121
122   // clear render target buffer
123   SDL_RenderClear(sdl_renderer);
124
125   // copy backbuffer to render target buffer
126   SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
127
128 #if !USE_FINAL_SCREEN_BITMAP
129   // copy global animations to render target buffer, if defined
130   if (gfx.draw_global_anim_function != NULL)
131     gfx.draw_global_anim_function();
132 #endif
133
134   // show render target buffer on screen
135   SDL_RenderPresent(sdl_renderer);
136
137 #else
138
139   if (rect)
140     SDL_UpdateWindowSurfaceRects(sdl_window, rect, 1);
141   else
142     SDL_UpdateWindowSurface(sdl_window);
143 #endif
144
145 #else   // TARGET_SDL
146   if (rect)
147     SDL_UpdateRects(backbuffer->surface, 1, rect);
148   else
149     SDL_UpdateRect(backbuffer->surface, 0, 0, 0, 0);
150 #endif
151 }
152
153 static void setFullscreenParameters(char *fullscreen_mode_string)
154 {
155 #if defined(TARGET_SDL2)
156   fullscreen_width = video.width;
157   fullscreen_height = video.height;
158   fullscreen_xoffset = 0;
159   fullscreen_yoffset = 0;  
160
161 #else
162
163   struct ScreenModeInfo *fullscreen_mode;
164   int i;
165
166   fullscreen_mode = get_screen_mode_from_string(fullscreen_mode_string);
167
168   if (fullscreen_mode == NULL)
169     return;
170
171   for (i = 0; video.fullscreen_modes[i].width != -1; i++)
172   {
173     if (fullscreen_mode->width  == video.fullscreen_modes[i].width &&
174         fullscreen_mode->height == video.fullscreen_modes[i].height)
175     {
176       fullscreen_width  = fullscreen_mode->width;
177       fullscreen_height = fullscreen_mode->height;
178
179       fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
180       fullscreen_yoffset = (fullscreen_height - video.height) / 2;
181
182       break;
183     }
184   }
185 #endif
186 }
187
188 static void SDLSetWindowIcon(char *basename)
189 {
190   /* (setting the window icon on Mac OS X would replace the high-quality
191      dock icon with the currently smaller (and uglier) icon from file) */
192
193 #if !defined(PLATFORM_MACOSX)
194   char *filename = getCustomImageFilename(basename);
195   SDL_Surface *surface;
196
197   if (filename == NULL)
198   {
199     Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
200
201     return;
202   }
203
204   if ((surface = IMG_Load(filename)) == NULL)
205   {
206     Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
207
208     return;
209   }
210
211   /* set transparent color */
212   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
213                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
214
215 #if defined(TARGET_SDL2)
216   SDL_SetWindowIcon(sdl_window, surface);
217 #else
218   SDL_WM_SetIcon(surface, NULL);
219 #endif
220 #endif
221 }
222
223 #if defined(TARGET_SDL2)
224
225 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
226                                    SDL_PixelFormat *format2)
227 {
228   return (format1->format        == format2->format &&
229           format1->BitsPerPixel  == format2->BitsPerPixel &&
230           format1->BytesPerPixel == format2->BytesPerPixel &&
231           format1->Rmask         == format2->Rmask &&
232           format1->Gmask         == format2->Gmask &&
233           format1->Bmask         == format2->Bmask &&
234           format1->Amask         == format2->Amask);
235 }
236
237 boolean SDLSetNativeSurface(SDL_Surface **surface)
238 {
239   SDL_Surface *new_surface;
240
241   if (surface == NULL ||
242       *surface == NULL ||
243       backbuffer == NULL ||
244       backbuffer->surface == NULL)
245     return FALSE;
246
247   // if pixel format already optimized for destination surface, do nothing
248   if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
249     return FALSE;
250
251   new_surface = SDL_ConvertSurface(*surface, backbuffer->surface->format, 0);
252
253   if (new_surface == NULL)
254     Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
255
256   SDL_FreeSurface(*surface);
257
258   *surface = new_surface;
259
260   return TRUE;
261 }
262
263 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
264 {
265   SDL_Surface *new_surface;
266
267   if (surface == NULL)
268     return NULL;
269
270   if (backbuffer && backbuffer->surface)
271     new_surface = SDL_ConvertSurface(surface, backbuffer->surface->format, 0);
272   else
273     new_surface = SDL_ConvertSurface(surface, surface->format, 0);
274
275   if (new_surface == NULL)
276     Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
277
278   return new_surface;
279 }
280
281 #else
282
283 boolean SDLSetNativeSurface(SDL_Surface **surface)
284 {
285   SDL_Surface *new_surface;
286
287   if (surface == NULL ||
288       *surface == NULL ||
289       !video.initialized)
290     return FALSE;
291
292   new_surface = SDL_DisplayFormat(*surface);
293
294   if (new_surface == NULL)
295     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
296
297   SDL_FreeSurface(*surface);
298
299   *surface = new_surface;
300
301   return TRUE;
302 }
303
304 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
305 {
306   SDL_Surface *new_surface;
307
308   if (video.initialized)
309     new_surface = SDL_DisplayFormat(surface);
310   else
311     new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
312
313   if (new_surface == NULL)
314     Error(ERR_EXIT, "%s() failed: %s",
315           (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
316           SDL_GetError());
317
318   return new_surface;
319 }
320
321 #endif
322
323 #if defined(TARGET_SDL2)
324 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
325 {
326   SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
327
328   if (texture == NULL)
329     Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
330           SDL_GetError());
331
332   return texture;
333 }
334 #endif
335
336 void SDLCreateBitmapTextures(Bitmap *bitmap)
337 {
338 #if defined(TARGET_SDL2)
339   if (bitmap == NULL)
340     return;
341
342   if (bitmap->texture)
343     SDL_DestroyTexture(bitmap->texture);
344   if (bitmap->texture_masked)
345     SDL_DestroyTexture(bitmap->texture_masked);
346
347   bitmap->texture        = SDLCreateTextureFromSurface(bitmap->surface);
348   bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
349 #endif
350 }
351
352 void SDLInitVideoDisplay(void)
353 {
354 #if !defined(TARGET_SDL2)
355   if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
356     SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
357
358   SDL_putenv("SDL_VIDEO_CENTERED=1");
359 #endif
360
361   /* initialize SDL video */
362   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
363     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
364
365   /* set default SDL depth */
366 #if !defined(TARGET_SDL2)
367   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
368 #else
369   video.default_depth = 32;     // (how to determine video depth in SDL2?)
370 #endif
371 }
372
373 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
374                         boolean fullscreen)
375 {
376 #if !defined(TARGET_SDL2)
377   static int screen_xy[][2] =
378   {
379     {  640, 480 },
380     {  800, 600 },
381     { 1024, 768 },
382     {   -1,  -1 }
383   };
384 #endif
385   SDL_Rect **modes = NULL;
386   boolean hardware_fullscreen_available = TRUE;
387   int i, j;
388
389   /* default: normal game window size */
390   fullscreen_width = video.width;
391   fullscreen_height = video.height;
392   fullscreen_xoffset = 0;
393   fullscreen_yoffset = 0;
394
395 #if !defined(TARGET_SDL2)
396   /* determine required standard fullscreen mode for game screen size */
397   for (i = 0; screen_xy[i][0] != -1; i++)
398   {
399     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
400     {
401       fullscreen_width  = screen_xy[i][0];
402       fullscreen_height = screen_xy[i][1];
403
404       break;
405     }
406   }
407
408   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
409   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
410 #endif
411
412   checked_free(video.fullscreen_modes);
413
414   video.fullscreen_modes = NULL;
415   video.fullscreen_mode_current = NULL;
416
417   video.window_scaling_percent = setup.window_scaling_percent;
418   video.window_scaling_quality = setup.window_scaling_quality;
419
420 #if defined(TARGET_SDL2)
421   int num_displays = SDL_GetNumVideoDisplays();
422
423   if (num_displays > 0)
424   {
425     // currently only display modes of first display supported
426     int num_modes = SDL_GetNumDisplayModes(0);
427
428     if (num_modes > 0)
429     {
430       modes = checked_calloc((num_modes + 1) * sizeof(SDL_Rect *));
431
432       for (i = 0; i < num_modes; i++)
433       {
434         SDL_DisplayMode mode;
435
436         if (SDL_GetDisplayMode(0, i, &mode) < 0)
437           break;
438
439         modes[i] = checked_calloc(sizeof(SDL_Rect));
440
441         modes[i]->w = mode.w;
442         modes[i]->h = mode.h;
443       }
444     }
445   }
446 #else
447   /* get available hardware supported fullscreen modes */
448   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
449 #endif
450
451   if (modes == NULL)
452   {
453     /* no hardware screen modes available => no fullscreen mode support */
454     // video.fullscreen_available = FALSE;
455     hardware_fullscreen_available = FALSE;
456   }
457   else if (modes == (SDL_Rect **)-1)
458   {
459     /* fullscreen resolution is not restricted -- all resolutions available */
460     video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
461
462     /* use native video buffer size for fullscreen mode */
463     video.fullscreen_modes[0].width  = video.width;
464     video.fullscreen_modes[0].height = video.height;
465
466     video.fullscreen_modes[1].width  = -1;
467     video.fullscreen_modes[1].height = -1;
468   }
469   else
470   {
471     /* in this case, a certain number of screen modes is available */
472     int num_modes = 0;
473
474     for (i = 0; modes[i] != NULL; i++)
475     {
476       boolean found_mode = FALSE;
477
478       /* screen mode is smaller than video buffer size -- skip it */
479       if (modes[i]->w < video.width || modes[i]->h < video.height)
480         continue;
481
482       if (video.fullscreen_modes != NULL)
483         for (j = 0; video.fullscreen_modes[j].width != -1; j++)
484           if (modes[i]->w == video.fullscreen_modes[j].width &&
485               modes[i]->h == video.fullscreen_modes[j].height)
486             found_mode = TRUE;
487
488       if (found_mode)           /* screen mode already stored -- skip it */
489         continue;
490
491       /* new mode found; add it to list of available fullscreen modes */
492
493       num_modes++;
494
495       video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
496                                                (num_modes + 1) *
497                                                sizeof(struct ScreenModeInfo));
498
499       video.fullscreen_modes[num_modes - 1].width  = modes[i]->w;
500       video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
501
502       video.fullscreen_modes[num_modes].width  = -1;
503       video.fullscreen_modes[num_modes].height = -1;
504     }
505
506     if (num_modes == 0)
507     {
508       /* no appropriate screen modes available => no fullscreen mode support */
509       // video.fullscreen_available = FALSE;
510       hardware_fullscreen_available = FALSE;
511     }
512   }
513
514   video.fullscreen_available = hardware_fullscreen_available;
515
516 #if USE_DESKTOP_FULLSCREEN
517   // in SDL 2.0, there is always support for desktop fullscreen mode
518   // (in SDL 1.2, there is only support for "real" fullscreen mode)
519   video.fullscreen_available = TRUE;
520 #endif
521
522 #if defined(TARGET_SDL2)
523   if (modes)
524   {
525     for (i = 0; modes[i] != NULL; i++)
526       checked_free(modes[i]);
527
528     checked_free(modes);
529   }
530 #endif
531
532   /* open SDL video output device (window or fullscreen mode) */
533   if (!SDLSetVideoMode(backbuffer, fullscreen))
534     Error(ERR_EXIT, "setting video mode failed");
535
536   /* !!! SDL2 can only set the window icon if the window already exists !!! */
537   /* set window icon */
538   SDLSetWindowIcon(program.icon_filename);
539
540   /* set window and icon title */
541 #if defined(TARGET_SDL2)
542   SDL_SetWindowTitle(sdl_window, program.window_title);
543 #else
544   SDL_WM_SetCaption(program.window_title, program.window_title);
545 #endif
546
547   /* SDL cannot directly draw to the visible video framebuffer like X11,
548      but always uses a backbuffer, which is then blitted to the visible
549      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
550      visible video framebuffer with 'SDL_Flip', if the hardware supports
551      this). Therefore do not use an additional backbuffer for drawing, but
552      use a symbolic buffer (distinguishable from the SDL backbuffer) called
553      'window', which indicates that the SDL backbuffer should be updated to
554      the visible video framebuffer when attempting to blit to it.
555
556      For convenience, it seems to be a good idea to create this symbolic
557      buffer 'window' at the same size as the SDL backbuffer. Although it
558      should never be drawn to directly, it would do no harm nevertheless. */
559
560   /* create additional (symbolic) buffer for double-buffering */
561   ReCreateBitmap(window, video.width, video.height, video.depth);
562 }
563
564 static SDL_Surface *SDLCreateScreen(DrawBuffer **backbuffer,
565                                     boolean fullscreen)
566 {
567   SDL_Surface *new_surface = NULL;
568
569 #if defined(TARGET_SDL2)
570   int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
571 #if USE_DESKTOP_FULLSCREEN
572   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
573 #else
574   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN;
575 #endif
576
577 #else
578   int surface_flags_window = SURFACE_FLAGS;
579   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
580 #endif
581
582   int width  = (fullscreen ? fullscreen_width  : video.width);
583   int height = (fullscreen ? fullscreen_height : video.height);
584   int surface_flags = (fullscreen ? surface_flags_fullscreen :
585                        surface_flags_window);
586
587   // default window size is unscaled
588   video.window_width  = video.width;
589   video.window_height = video.height;
590
591 #if defined(TARGET_SDL2)
592
593   // store if initial screen mode is fullscreen mode when changing screen size
594   video.fullscreen_initial = fullscreen;
595
596 #if USE_RENDERER
597   float window_scaling_factor = (float)setup.window_scaling_percent / 100;
598 #if !USE_DESKTOP_FULLSCREEN
599   float screen_scaling_factor = (fullscreen ? 1 : window_scaling_factor);
600 #endif
601
602   video.window_width  = window_scaling_factor * width;
603   video.window_height = window_scaling_factor * height;
604
605   if ((*backbuffer)->surface)
606   {
607     SDL_FreeSurface((*backbuffer)->surface);
608     (*backbuffer)->surface = NULL;
609   }
610
611   if (sdl_texture)
612   {
613     SDL_DestroyTexture(sdl_texture);
614     sdl_texture = NULL;
615   }
616
617   if (!(fullscreen && fullscreen_enabled))
618   {
619     if (sdl_renderer)
620     {
621       SDL_DestroyRenderer(sdl_renderer);
622       sdl_renderer = NULL;
623     }
624
625     if (sdl_window)
626     {
627       SDL_DestroyWindow(sdl_window);
628       sdl_window = NULL;
629     }
630   }
631
632   if (sdl_window == NULL)
633     sdl_window = SDL_CreateWindow(program.window_title,
634                                   SDL_WINDOWPOS_CENTERED,
635                                   SDL_WINDOWPOS_CENTERED,
636 #if USE_DESKTOP_FULLSCREEN
637                                   video.window_width,
638                                   video.window_height,
639 #else
640                                   (int)(screen_scaling_factor * width),
641                                   (int)(screen_scaling_factor * height),
642 #endif
643                                   surface_flags);
644
645   if (sdl_window != NULL)
646   {
647 #if 0
648     /* if SDL_CreateRenderer() is called from within a VirtualBox Windows VM
649      *without* enabling 2D/3D acceleration and/or guest additions installed,
650      it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
651      it will try to use accelerated graphics and apparently fails miserably) */
652     if (sdl_renderer == NULL)
653       sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_SOFTWARE);
654 #else
655     if (sdl_renderer == NULL)
656       sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
657 #endif
658
659     if (sdl_renderer != NULL)
660     {
661       SDL_RenderSetLogicalSize(sdl_renderer, width, height);
662       // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
663       SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
664
665       sdl_texture = SDL_CreateTexture(sdl_renderer,
666                                       SDL_PIXELFORMAT_ARGB8888,
667                                       SDL_TEXTUREACCESS_STREAMING,
668                                       width, height);
669
670       if (sdl_texture != NULL)
671       {
672         // use SDL default values for RGB masks and no alpha channel
673         new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
674
675         if (new_surface == NULL)
676           Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s",
677                 SDL_GetError());
678       }
679       else
680       {
681         Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
682       }
683     }
684     else
685     {
686       Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
687     }
688   }
689   else
690   {
691     Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
692   }
693
694 #else
695
696   if (sdl_window)
697     SDL_DestroyWindow(sdl_window);
698
699   sdl_window = SDL_CreateWindow(program.window_title,
700                                 SDL_WINDOWPOS_CENTERED,
701                                 SDL_WINDOWPOS_CENTERED,
702                                 width, height,
703                                 surface_flags);
704
705   if (sdl_window != NULL)
706     new_surface = SDL_GetWindowSurface(sdl_window);
707 #endif
708
709 #else
710   new_surface = SDL_SetVideoMode(width, height, video.depth, surface_flags);
711 #endif
712
713 #if defined(TARGET_SDL2)
714   // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
715   if (new_surface != NULL)
716     fullscreen_enabled = fullscreen;
717 #endif
718
719   return new_surface;
720 }
721
722 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
723 {
724   boolean success = TRUE;
725   SDL_Surface *new_surface = NULL;
726
727   SetWindowTitle();
728
729   if (*backbuffer == NULL)
730     *backbuffer = CreateBitmapStruct();
731
732   /* (real bitmap might be larger in fullscreen mode with video offsets) */
733   (*backbuffer)->width  = video.width;
734   (*backbuffer)->height = video.height;
735
736   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
737   {
738     setFullscreenParameters(setup.fullscreen_mode);
739
740     video_xoffset = fullscreen_xoffset;
741     video_yoffset = fullscreen_yoffset;
742
743     /* switch display to fullscreen mode, if available */
744     new_surface = SDLCreateScreen(backbuffer, TRUE);
745
746     if (new_surface == NULL)
747     {
748       /* switching display to fullscreen mode failed */
749       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
750
751       /* do not try it again */
752       video.fullscreen_available = FALSE;
753
754       success = FALSE;
755     }
756     else
757     {
758       (*backbuffer)->surface = new_surface;
759
760       video.fullscreen_enabled = TRUE;
761       video.fullscreen_mode_current = setup.fullscreen_mode;
762
763       success = TRUE;
764     }
765   }
766
767   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
768   {
769     video_xoffset = 0;
770     video_yoffset = 0;
771
772     /* switch display to window mode */
773     new_surface = SDLCreateScreen(backbuffer, FALSE);
774
775     if (new_surface == NULL)
776     {
777       /* switching display to window mode failed -- should not happen */
778       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
779
780       success = FALSE;
781     }
782     else
783     {
784       (*backbuffer)->surface = new_surface;
785
786       video.fullscreen_enabled = FALSE;
787       video.window_scaling_percent = setup.window_scaling_percent;
788       video.window_scaling_quality = setup.window_scaling_quality;
789
790       success = TRUE;
791     }
792   }
793
794 #if defined(TARGET_SDL2)
795   SDLRedrawWindow();                    // map window
796 #endif
797
798 #ifdef DEBUG
799 #if defined(PLATFORM_WIN32)
800   // experimental drag and drop code
801
802   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
803
804   {
805     SDL_SysWMinfo wminfo;
806     HWND hwnd;
807     boolean wminfo_success = FALSE;
808
809     SDL_VERSION(&wminfo.version);
810 #if defined(TARGET_SDL2)
811     if (sdl_window)
812       wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
813 #else
814     wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
815 #endif
816
817     if (wminfo_success)
818     {
819 #if defined(TARGET_SDL2)
820       hwnd = wminfo.info.win.window;
821 #else
822       hwnd = wminfo.window;
823 #endif
824
825       DragAcceptFiles(hwnd, TRUE);
826     }
827   }
828 #endif
829 #endif
830
831   return success;
832 }
833
834 void SDLSetWindowTitle()
835 {
836 #if defined(TARGET_SDL2)
837   SDL_SetWindowTitle(sdl_window, program.window_title);
838 #else
839   SDL_WM_SetCaption(program.window_title, program.window_title);
840 #endif
841 }
842
843 #if defined(TARGET_SDL2)
844 void SDLSetWindowScaling(int window_scaling_percent)
845 {
846   if (sdl_window == NULL)
847     return;
848
849   float window_scaling_factor = (float)window_scaling_percent / 100;
850   int new_window_width  = (int)(window_scaling_factor * video.width);
851   int new_window_height = (int)(window_scaling_factor * video.height);
852
853   SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
854
855   video.window_scaling_percent = window_scaling_percent;
856   video.window_width  = new_window_width;
857   video.window_height = new_window_height;
858
859   SetWindowTitle();
860 }
861
862 void SDLSetWindowScalingQuality(char *window_scaling_quality)
863 {
864   if (sdl_texture == NULL)
865     return;
866
867   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
868
869   SDL_Texture *new_texture = SDL_CreateTexture(sdl_renderer,
870                                                SDL_PIXELFORMAT_ARGB8888,
871                                                SDL_TEXTUREACCESS_STREAMING,
872                                                video.width, video.height);
873
874   if (new_texture != NULL)
875   {
876     SDL_DestroyTexture(sdl_texture);
877
878     sdl_texture = new_texture;
879
880     SDLRedrawWindow();
881   }
882
883   video.window_scaling_quality = window_scaling_quality;
884 }
885
886 void SDLSetWindowFullscreen(boolean fullscreen)
887 {
888   if (sdl_window == NULL)
889     return;
890
891 #if USE_DESKTOP_FULLSCREEN
892   int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
893 #else
894   int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
895 #endif
896
897   if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
898     video.fullscreen_enabled = fullscreen_enabled = fullscreen;
899
900   // if screen size was changed in fullscreen mode, correct desktop window size
901   if (!fullscreen && video.fullscreen_initial)
902   {
903     SDLSetWindowScaling(setup.window_scaling_percent);
904     SDL_SetWindowPosition(sdl_window,
905                           SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
906
907     video.fullscreen_initial = FALSE;
908   }
909 }
910
911 void SDLRedrawWindow()
912 {
913   UpdateScreen(NULL);
914 }
915 #endif
916
917 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
918                             int depth)
919 {
920   SDL_Surface *surface =
921     SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
922
923   if (surface == NULL)
924     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
925
926   SDLSetNativeSurface(&surface);
927
928   bitmap->surface = surface;
929 }
930
931 void SDLFreeBitmapPointers(Bitmap *bitmap)
932 {
933   if (bitmap->surface)
934     SDL_FreeSurface(bitmap->surface);
935   if (bitmap->surface_masked)
936     SDL_FreeSurface(bitmap->surface_masked);
937
938   bitmap->surface = NULL;
939   bitmap->surface_masked = NULL;
940
941 #if defined(TARGET_SDL2)
942   if (bitmap->texture)
943     SDL_DestroyTexture(bitmap->texture);
944   if (bitmap->texture_masked)
945     SDL_DestroyTexture(bitmap->texture_masked);
946
947   bitmap->texture = NULL;
948   bitmap->texture_masked = NULL;
949 #endif
950 }
951
952 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
953                  int src_x, int src_y, int width, int height,
954                  int dst_x, int dst_y, int mask_mode)
955 {
956   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
957   SDL_Rect src_rect, dst_rect;
958
959   if (src_bitmap == backbuffer)
960   {
961     src_x += video_xoffset;
962     src_y += video_yoffset;
963   }
964
965   src_rect.x = src_x;
966   src_rect.y = src_y;
967   src_rect.w = width;
968   src_rect.h = height;
969
970   if (dst_bitmap == backbuffer || dst_bitmap == window)
971   {
972     dst_x += video_xoffset;
973     dst_y += video_yoffset;
974   }
975
976   dst_rect.x = dst_x;
977   dst_rect.y = dst_y;
978   dst_rect.w = width;
979   dst_rect.h = height;
980
981   // if (src_bitmap != backbuffer || dst_bitmap != window)
982   if (!(src_bitmap == backbuffer && dst_bitmap == window))
983     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
984                      src_bitmap->surface_masked : src_bitmap->surface),
985                     &src_rect, real_dst_bitmap->surface, &dst_rect);
986
987 #if defined(TARGET_SDL2)
988   if (dst_bitmap == window)
989   {
990     // SDL_UpdateWindowSurface(sdl_window);
991     // SDL_UpdateWindowSurfaceRects(sdl_window, &dst_rect, 1);
992     UpdateScreen(&dst_rect);
993   }
994 #else
995   if (dst_bitmap == window)
996   {
997     // SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
998     UpdateScreen(&dst_rect);
999   }
1000 #endif
1001 }
1002
1003 void SDLBlitTexture(Bitmap *bitmap,
1004                     int src_x, int src_y, int width, int height,
1005                     int dst_x, int dst_y, int mask_mode)
1006 {
1007 #if defined(TARGET_SDL2)
1008 #if USE_RENDERER
1009   SDL_Texture *texture;
1010   SDL_Rect src_rect;
1011   SDL_Rect dst_rect;
1012
1013   texture =
1014     (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
1015
1016   if (texture == NULL)
1017     return;
1018
1019   src_rect.x = src_x;
1020   src_rect.y = src_y;
1021   src_rect.w = width;
1022   src_rect.h = height;
1023
1024   dst_rect.x = dst_x;
1025   dst_rect.y = dst_y;
1026   dst_rect.w = width;
1027   dst_rect.h = height;
1028
1029   SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
1030 #endif
1031 #endif
1032 }
1033
1034 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
1035                       Uint32 color)
1036 {
1037   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
1038   SDL_Rect rect;
1039
1040   if (dst_bitmap == backbuffer || dst_bitmap == window)
1041   {
1042     x += video_xoffset;
1043     y += video_yoffset;
1044   }
1045
1046   rect.x = x;
1047   rect.y = y;
1048   rect.w = width;
1049   rect.h = height;
1050
1051   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
1052
1053 #if defined(TARGET_SDL2)
1054   if (dst_bitmap == window)
1055   {
1056     // SDL_UpdateWindowSurface(sdl_window);
1057     // SDL_UpdateWindowSurfaceRects(sdl_window, &rect, 1);
1058     UpdateScreen(&rect);
1059   }
1060 #else
1061   if (dst_bitmap == window)
1062   {
1063     // SDL_UpdateRect(backbuffer->surface, x, y, width, height);
1064     UpdateScreen(&rect);
1065   }
1066 #endif
1067 }
1068
1069 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
1070                       int fade_mode, int fade_delay, int post_delay,
1071                       void (*draw_border_function)(void))
1072 {
1073   static boolean initialization_needed = TRUE;
1074   static SDL_Surface *surface_source = NULL;
1075   static SDL_Surface *surface_target = NULL;
1076   static SDL_Surface *surface_black = NULL;
1077   SDL_Surface *surface_screen = backbuffer->surface;
1078   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
1079   SDL_Rect src_rect, dst_rect;
1080   SDL_Rect dst_rect2;
1081   int src_x = x, src_y = y;
1082   int dst_x = x, dst_y = y;
1083   unsigned int time_last, time_current;
1084
1085   /* check if screen size has changed */
1086   if (surface_source != NULL && (video.width  != surface_source->w ||
1087                                  video.height != surface_source->h))
1088   {
1089     SDL_FreeSurface(surface_source);
1090     SDL_FreeSurface(surface_target);
1091     SDL_FreeSurface(surface_black);
1092
1093     initialization_needed = TRUE;
1094   }
1095
1096   src_rect.x = src_x;
1097   src_rect.y = src_y;
1098   src_rect.w = width;
1099   src_rect.h = height;
1100
1101   dst_x += video_xoffset;
1102   dst_y += video_yoffset;
1103
1104   dst_rect.x = dst_x;
1105   dst_rect.y = dst_y;
1106   dst_rect.w = width;           /* (ignored) */
1107   dst_rect.h = height;          /* (ignored) */
1108
1109   dst_rect2 = dst_rect;
1110
1111   if (initialization_needed)
1112   {
1113 #if defined(TARGET_SDL2)
1114     unsigned int flags = 0;
1115 #else
1116     unsigned int flags = SDL_SRCALPHA;
1117
1118     /* use same surface type as screen surface */
1119     if ((surface_screen->flags & SDL_HWSURFACE))
1120       flags |= SDL_HWSURFACE;
1121     else
1122       flags |= SDL_SWSURFACE;
1123 #endif
1124
1125     /* create surface for temporary copy of screen buffer (source) */
1126     if ((surface_source =
1127          SDL_CreateRGBSurface(flags,
1128                               video.width,
1129                               video.height,
1130                               surface_screen->format->BitsPerPixel,
1131                               surface_screen->format->Rmask,
1132                               surface_screen->format->Gmask,
1133                               surface_screen->format->Bmask,
1134                               surface_screen->format->Amask)) == NULL)
1135       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1136
1137     /* create surface for cross-fading screen buffer (target) */
1138     if ((surface_target =
1139          SDL_CreateRGBSurface(flags,
1140                               video.width,
1141                               video.height,
1142                               surface_screen->format->BitsPerPixel,
1143                               surface_screen->format->Rmask,
1144                               surface_screen->format->Gmask,
1145                               surface_screen->format->Bmask,
1146                               surface_screen->format->Amask)) == NULL)
1147       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1148
1149     /* create black surface for fading from/to black */
1150     if ((surface_black =
1151          SDL_CreateRGBSurface(flags,
1152                               video.width,
1153                               video.height,
1154                               surface_screen->format->BitsPerPixel,
1155                               surface_screen->format->Rmask,
1156                               surface_screen->format->Gmask,
1157                               surface_screen->format->Bmask,
1158                               surface_screen->format->Amask)) == NULL)
1159       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
1160
1161     /* completely fill the surface with black color pixels */
1162     SDL_FillRect(surface_black, NULL,
1163                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
1164
1165     initialization_needed = FALSE;
1166   }
1167
1168   /* copy source and target surfaces to temporary surfaces for fading */
1169   if (fade_mode & FADE_TYPE_TRANSFORM)
1170   {
1171     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
1172     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
1173   }
1174   else if (fade_mode & FADE_TYPE_FADE_IN)
1175   {
1176     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
1177     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
1178   }
1179   else          /* FADE_TYPE_FADE_OUT */
1180   {
1181     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
1182     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
1183   }
1184
1185   time_current = SDL_GetTicks();
1186
1187   if (fade_mode == FADE_MODE_MELT)
1188   {
1189     boolean done = FALSE;
1190     int melt_pixels = 2;
1191     int melt_columns = width / melt_pixels;
1192     int ypos[melt_columns];
1193     int max_steps = height / 8 + 32;
1194     int steps_done = 0;
1195     float steps = 0;
1196     int i;
1197
1198     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1199 #if defined(TARGET_SDL2)
1200     SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_NONE);
1201 #else
1202     SDL_SetAlpha(surface_target, 0, 0);         /* disable alpha blending */
1203 #endif
1204
1205     ypos[0] = -GetSimpleRandom(16);
1206
1207     for (i = 1 ; i < melt_columns; i++)
1208     {
1209       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
1210
1211       ypos[i] = ypos[i - 1] + r;
1212
1213       if (ypos[i] > 0)
1214         ypos[i] = 0;
1215       else
1216         if (ypos[i] == -16)
1217           ypos[i] = -15;
1218     }
1219
1220     while (!done)
1221     {
1222       int steps_final;
1223
1224       time_last = time_current;
1225       time_current = SDL_GetTicks();
1226       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1227       steps_final = MIN(MAX(0, steps), max_steps);
1228
1229       steps_done++;
1230
1231       done = (steps_done >= steps_final);
1232
1233       for (i = 0 ; i < melt_columns; i++)
1234       {
1235         if (ypos[i] < 0)
1236         {
1237           ypos[i]++;
1238
1239           done = FALSE;
1240         }
1241         else if (ypos[i] < height)
1242         {
1243           int y1 = 16;
1244           int y2 = 8;
1245           int y3 = 8;
1246           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1247
1248           if (ypos[i] + dy >= height)
1249             dy = height - ypos[i];
1250
1251           /* copy part of (appearing) target surface to upper area */
1252           src_rect.x = src_x + i * melt_pixels;
1253           // src_rect.y = src_y + ypos[i];
1254           src_rect.y = src_y;
1255           src_rect.w = melt_pixels;
1256           // src_rect.h = dy;
1257           src_rect.h = ypos[i] + dy;
1258
1259           dst_rect.x = dst_x + i * melt_pixels;
1260           // dst_rect.y = dst_y + ypos[i];
1261           dst_rect.y = dst_y;
1262
1263           if (steps_done >= steps_final)
1264             SDL_BlitSurface(surface_target, &src_rect,
1265                             surface_screen, &dst_rect);
1266
1267           ypos[i] += dy;
1268
1269           /* copy part of (disappearing) source surface to lower area */
1270           src_rect.x = src_x + i * melt_pixels;
1271           src_rect.y = src_y;
1272           src_rect.w = melt_pixels;
1273           src_rect.h = height - ypos[i];
1274
1275           dst_rect.x = dst_x + i * melt_pixels;
1276           dst_rect.y = dst_y + ypos[i];
1277
1278           if (steps_done >= steps_final)
1279             SDL_BlitSurface(surface_source, &src_rect,
1280                             surface_screen, &dst_rect);
1281
1282           done = FALSE;
1283         }
1284         else
1285         {
1286           src_rect.x = src_x + i * melt_pixels;
1287           src_rect.y = src_y;
1288           src_rect.w = melt_pixels;
1289           src_rect.h = height;
1290
1291           dst_rect.x = dst_x + i * melt_pixels;
1292           dst_rect.y = dst_y;
1293
1294           if (steps_done >= steps_final)
1295             SDL_BlitSurface(surface_target, &src_rect,
1296                             surface_screen, &dst_rect);
1297         }
1298       }
1299
1300       if (steps_done >= steps_final)
1301       {
1302         if (draw_border_function != NULL)
1303           draw_border_function();
1304
1305         UpdateScreen(&dst_rect2);
1306       }
1307     }
1308   }
1309   else if (fade_mode == FADE_MODE_CURTAIN)
1310   {
1311     float xx;
1312     int xx_final;
1313     int xx_size = width / 2;
1314
1315     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1316 #if defined(TARGET_SDL2)
1317     SDL_SetSurfaceBlendMode(surface_source, SDL_BLENDMODE_NONE);
1318 #else
1319     SDL_SetAlpha(surface_source, 0, 0);         /* disable alpha blending */
1320 #endif
1321
1322     for (xx = 0; xx < xx_size;)
1323     {
1324       time_last = time_current;
1325       time_current = SDL_GetTicks();
1326       xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1327       xx_final = MIN(MAX(0, xx), xx_size);
1328
1329       src_rect.x = src_x;
1330       src_rect.y = src_y;
1331       src_rect.w = width;
1332       src_rect.h = height;
1333
1334       dst_rect.x = dst_x;
1335       dst_rect.y = dst_y;
1336
1337       /* draw new (target) image to screen buffer */
1338       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1339
1340       if (xx_final < xx_size)
1341       {
1342         src_rect.w = xx_size - xx_final;
1343         src_rect.h = height;
1344
1345         /* draw old (source) image to screen buffer (left side) */
1346
1347         src_rect.x = src_x + xx_final;
1348         dst_rect.x = dst_x;
1349
1350         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1351
1352         /* draw old (source) image to screen buffer (right side) */
1353
1354         src_rect.x = src_x + xx_size;
1355         dst_rect.x = dst_x + xx_size + xx_final;
1356
1357         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1358       }
1359
1360       if (draw_border_function != NULL)
1361         draw_border_function();
1362
1363       /* only update the region of the screen that is affected from fading */
1364       UpdateScreen(&dst_rect2);
1365     }
1366   }
1367   else          /* fading in, fading out or cross-fading */
1368   {
1369     float alpha;
1370     int alpha_final;
1371
1372     for (alpha = 0.0; alpha < 255.0;)
1373     {
1374       time_last = time_current;
1375       time_current = SDL_GetTicks();
1376       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1377       alpha_final = MIN(MAX(0, alpha), 255);
1378
1379       /* draw existing (source) image to screen buffer */
1380       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1381
1382       /* draw new (target) image to screen buffer using alpha blending */
1383 #if defined(TARGET_SDL2)
1384       SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
1385       SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
1386 #else
1387       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
1388 #endif
1389       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1390
1391       if (draw_border_function != NULL)
1392         draw_border_function();
1393
1394       /* only update the region of the screen that is affected from fading */
1395       UpdateScreen(&dst_rect);
1396     }
1397   }
1398
1399   Delay(post_delay);
1400 }
1401
1402 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1403                        int to_x, int to_y, Uint32 color)
1404 {
1405   SDL_Surface *surface = dst_bitmap->surface;
1406   SDL_Rect rect;
1407
1408   if (from_x > to_x)
1409     swap_numbers(&from_x, &to_x);
1410
1411   if (from_y > to_y)
1412     swap_numbers(&from_y, &to_y);
1413
1414   rect.x = from_x;
1415   rect.y = from_y;
1416   rect.w = (to_x - from_x + 1);
1417   rect.h = (to_y - from_y + 1);
1418
1419   if (dst_bitmap == backbuffer || dst_bitmap == window)
1420   {
1421     rect.x += video_xoffset;
1422     rect.y += video_yoffset;
1423   }
1424
1425   SDL_FillRect(surface, &rect, color);
1426 }
1427
1428 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1429                  int to_x, int to_y, Uint32 color)
1430 {
1431   if (dst_bitmap == backbuffer || dst_bitmap == window)
1432   {
1433     from_x += video_xoffset;
1434     from_y += video_yoffset;
1435     to_x += video_xoffset;
1436     to_y += video_yoffset;
1437   }
1438
1439   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1440 }
1441
1442 #if ENABLE_UNUSED_CODE
1443 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1444                   int num_points, Uint32 color)
1445 {
1446   int i, x, y;
1447   int line_width = 4;
1448
1449   for (i = 0; i < num_points - 1; i++)
1450   {
1451     for (x = 0; x < line_width; x++)
1452     {
1453       for (y = 0; y < line_width; y++)
1454       {
1455         int dx = x - line_width / 2;
1456         int dy = y - line_width / 2;
1457
1458         if ((x == 0 && y == 0) ||
1459             (x == 0 && y == line_width - 1) ||
1460             (x == line_width - 1 && y == 0) ||
1461             (x == line_width - 1 && y == line_width - 1))
1462           continue;
1463
1464         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1465                  points[i+1].x + dx, points[i+1].y + dy, color);
1466       }
1467     }
1468   }
1469 }
1470 #endif
1471
1472 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1473 {
1474   SDL_Surface *surface = src_bitmap->surface;
1475
1476   if (src_bitmap == backbuffer || src_bitmap == window)
1477   {
1478     x += video_xoffset;
1479     y += video_yoffset;
1480   }
1481
1482   switch (surface->format->BytesPerPixel)
1483   {
1484     case 1:             /* assuming 8-bpp */
1485     {
1486       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1487     }
1488     break;
1489
1490     case 2:             /* probably 15-bpp or 16-bpp */
1491     {
1492       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1493     }
1494     break;
1495
1496   case 3:               /* slow 24-bpp mode; usually not used */
1497     {
1498       /* does this work? */
1499       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1500       Uint32 color = 0;
1501       int shift;
1502
1503       shift = surface->format->Rshift;
1504       color |= *(pix + shift / 8) >> shift;
1505       shift = surface->format->Gshift;
1506       color |= *(pix + shift / 8) >> shift;
1507       shift = surface->format->Bshift;
1508       color |= *(pix + shift / 8) >> shift;
1509
1510       return color;
1511     }
1512     break;
1513
1514   case 4:               /* probably 32-bpp */
1515     {
1516       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1517     }
1518     break;
1519   }
1520
1521   return 0;
1522 }
1523
1524
1525 /* ========================================================================= */
1526 /* The following functions were taken from the SGE library                   */
1527 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1528 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1529 /* ========================================================================= */
1530
1531 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1532 {
1533   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1534   {
1535     switch (surface->format->BytesPerPixel)
1536     {
1537       case 1:
1538       {
1539         /* Assuming 8-bpp */
1540         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1541       }
1542       break;
1543
1544       case 2:
1545       {
1546         /* Probably 15-bpp or 16-bpp */
1547         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1548       }
1549       break;
1550
1551       case 3:
1552       {
1553         /* Slow 24-bpp mode, usually not used */
1554         Uint8 *pix;
1555         int shift;
1556
1557         /* Gack - slow, but endian correct */
1558         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1559         shift = surface->format->Rshift;
1560         *(pix+shift/8) = color>>shift;
1561         shift = surface->format->Gshift;
1562         *(pix+shift/8) = color>>shift;
1563         shift = surface->format->Bshift;
1564         *(pix+shift/8) = color>>shift;
1565       }
1566       break;
1567
1568       case 4:
1569       {
1570         /* Probably 32-bpp */
1571         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1572       }
1573       break;
1574     }
1575   }
1576 }
1577
1578 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1579                   Uint8 R, Uint8 G, Uint8 B)
1580 {
1581   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1582 }
1583
1584 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1585 {
1586   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1587 }
1588
1589 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1590 {
1591   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1592 }
1593
1594 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1595 {
1596   Uint8 *pix;
1597   int shift;
1598
1599   /* Gack - slow, but endian correct */
1600   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1601   shift = surface->format->Rshift;
1602   *(pix+shift/8) = color>>shift;
1603   shift = surface->format->Gshift;
1604   *(pix+shift/8) = color>>shift;
1605   shift = surface->format->Bshift;
1606   *(pix+shift/8) = color>>shift;
1607 }
1608
1609 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1610 {
1611   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1612 }
1613
1614 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1615 {
1616   switch (dest->format->BytesPerPixel)
1617   {
1618     case 1:
1619       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1620       break;
1621
1622     case 2:
1623       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1624       break;
1625
1626     case 3:
1627       _PutPixel24(dest,x,y,color);
1628       break;
1629
1630     case 4:
1631       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1632       break;
1633   }
1634 }
1635
1636 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1637 {
1638   if (SDL_MUSTLOCK(surface))
1639   {
1640     if (SDL_LockSurface(surface) < 0)
1641     {
1642       return;
1643     }
1644   }
1645
1646   _PutPixel(surface, x, y, color);
1647
1648   if (SDL_MUSTLOCK(surface))
1649   {
1650     SDL_UnlockSurface(surface);
1651   }
1652 }
1653
1654 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1655                   Uint8 r, Uint8 g, Uint8 b)
1656 {
1657   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1658 }
1659
1660 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1661 {
1662   if (y >= 0 && y <= dest->h - 1)
1663   {
1664     switch (dest->format->BytesPerPixel)
1665     {
1666       case 1:
1667         return y*dest->pitch;
1668         break;
1669
1670       case 2:
1671         return y*dest->pitch/2;
1672         break;
1673
1674       case 3:
1675         return y*dest->pitch;
1676         break;
1677
1678       case 4:
1679         return y*dest->pitch/4;
1680         break;
1681     }
1682   }
1683
1684   return -1;
1685 }
1686
1687 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1688 {
1689   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1690   {
1691     switch (surface->format->BytesPerPixel)
1692     {
1693       case 1:
1694       {
1695         /* Assuming 8-bpp */
1696         *((Uint8 *)surface->pixels + ypitch + x) = color;
1697       }
1698       break;
1699
1700       case 2:
1701       {
1702         /* Probably 15-bpp or 16-bpp */
1703         *((Uint16 *)surface->pixels + ypitch + x) = color;
1704       }
1705       break;
1706
1707       case 3:
1708       {
1709         /* Slow 24-bpp mode, usually not used */
1710         Uint8 *pix;
1711         int shift;
1712
1713         /* Gack - slow, but endian correct */
1714         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1715         shift = surface->format->Rshift;
1716         *(pix+shift/8) = color>>shift;
1717         shift = surface->format->Gshift;
1718         *(pix+shift/8) = color>>shift;
1719         shift = surface->format->Bshift;
1720         *(pix+shift/8) = color>>shift;
1721       }
1722       break;
1723
1724       case 4:
1725       {
1726         /* Probably 32-bpp */
1727         *((Uint32 *)surface->pixels + ypitch + x) = color;
1728       }
1729       break;
1730     }
1731   }
1732 }
1733
1734 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1735                Uint32 Color)
1736 {
1737   SDL_Rect l;
1738
1739   if (SDL_MUSTLOCK(Surface))
1740   {
1741     if (SDL_LockSurface(Surface) < 0)
1742     {
1743       return;
1744     }
1745   }
1746
1747   if (x1 > x2)
1748   {
1749     Sint16 tmp = x1;
1750     x1 = x2;
1751     x2 = tmp;
1752   }
1753
1754   /* Do the clipping */
1755   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1756     return;
1757   if (x1 < 0)
1758     x1 = 0;
1759   if (x2 > Surface->w - 1)
1760     x2 = Surface->w - 1;
1761
1762   l.x = x1;
1763   l.y = y;
1764   l.w = x2 - x1 + 1;
1765   l.h = 1;
1766
1767   SDL_FillRect(Surface, &l, Color);
1768
1769   if (SDL_MUSTLOCK(Surface))
1770   {
1771     SDL_UnlockSurface(Surface);
1772   }
1773 }
1774
1775 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1776                   Uint8 R, Uint8 G, Uint8 B)
1777 {
1778   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1779 }
1780
1781 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1782 {
1783   SDL_Rect l;
1784
1785   if (x1 > x2)
1786   {
1787     Sint16 tmp = x1;
1788     x1 = x2;
1789     x2 = tmp;
1790   }
1791
1792   /* Do the clipping */
1793   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1794     return;
1795   if (x1 < 0)
1796     x1 = 0;
1797   if (x2 > Surface->w - 1)
1798     x2 = Surface->w - 1;
1799
1800   l.x = x1;
1801   l.y = y;
1802   l.w = x2 - x1 + 1;
1803   l.h = 1;
1804
1805   SDL_FillRect(Surface, &l, Color);
1806 }
1807
1808 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1809                Uint32 Color)
1810 {
1811   SDL_Rect l;
1812
1813   if (SDL_MUSTLOCK(Surface))
1814   {
1815     if (SDL_LockSurface(Surface) < 0)
1816     {
1817       return;
1818     }
1819   }
1820
1821   if (y1 > y2)
1822   {
1823     Sint16 tmp = y1;
1824     y1 = y2;
1825     y2 = tmp;
1826   }
1827
1828   /* Do the clipping */
1829   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1830     return;
1831   if (y1 < 0)
1832     y1 = 0;
1833   if (y2 > Surface->h - 1)
1834     y2 = Surface->h - 1;
1835
1836   l.x = x;
1837   l.y = y1;
1838   l.w = 1;
1839   l.h = y2 - y1 + 1;
1840
1841   SDL_FillRect(Surface, &l, Color);
1842
1843   if (SDL_MUSTLOCK(Surface))
1844   {
1845     SDL_UnlockSurface(Surface);
1846   }
1847 }
1848
1849 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1850                   Uint8 R, Uint8 G, Uint8 B)
1851 {
1852   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1853 }
1854
1855 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1856 {
1857   SDL_Rect l;
1858
1859   if (y1 > y2)
1860   {
1861     Sint16 tmp = y1;
1862     y1 = y2;
1863     y2 = tmp;
1864   }
1865
1866   /* Do the clipping */
1867   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1868     return;
1869   if (y1 < 0)
1870     y1 = 0;
1871   if (y2 > Surface->h - 1)
1872     y2 = Surface->h - 1;
1873
1874   l.x = x;
1875   l.y = y1;
1876   l.w = 1;
1877   l.h = y2 - y1 + 1;
1878
1879   SDL_FillRect(Surface, &l, Color);
1880 }
1881
1882 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1883                 Sint16 x2, Sint16 y2, Uint32 Color,
1884                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1885                               Uint32 Color))
1886 {
1887   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1888
1889   dx = x2 - x1;
1890   dy = y2 - y1;
1891
1892   sdx = (dx < 0) ? -1 : 1;
1893   sdy = (dy < 0) ? -1 : 1;
1894
1895   dx = sdx * dx + 1;
1896   dy = sdy * dy + 1;
1897
1898   x = y = 0;
1899
1900   px = x1;
1901   py = y1;
1902
1903   if (dx >= dy)
1904   {
1905     for (x = 0; x < dx; x++)
1906     {
1907       Callback(Surface, px, py, Color);
1908
1909       y += dy;
1910       if (y >= dx)
1911       {
1912         y -= dx;
1913         py += sdy;
1914       }
1915
1916       px += sdx;
1917     }
1918   }
1919   else
1920   {
1921     for (y = 0; y < dy; y++)
1922     {
1923       Callback(Surface, px, py, Color);
1924
1925       x += dx;
1926       if (x >= dy)
1927       {
1928         x -= dy;
1929         px += sdx;
1930       }
1931
1932       py += sdy;
1933     }
1934   }
1935 }
1936
1937 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1938                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1939                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1940                                  Uint32 Color))
1941 {
1942   sge_DoLine(Surface, X1, Y1, X2, Y2,
1943              SDL_MapRGB(Surface->format, R, G, B), Callback);
1944 }
1945
1946 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1947               Uint32 Color)
1948 {
1949   if (SDL_MUSTLOCK(Surface))
1950   {
1951     if (SDL_LockSurface(Surface) < 0)
1952       return;
1953    }
1954
1955    /* Draw the line */
1956    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1957
1958    /* unlock the display */
1959    if (SDL_MUSTLOCK(Surface))
1960    {
1961       SDL_UnlockSurface(Surface);
1962    }
1963 }
1964
1965 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1966                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1967 {
1968   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1969 }
1970
1971 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1972 {
1973   if (dst_bitmap == backbuffer || dst_bitmap == window)
1974   {
1975     x += video_xoffset;
1976     y += video_yoffset;
1977   }
1978
1979   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1980 }
1981
1982
1983 /*
1984   -----------------------------------------------------------------------------
1985   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1986   -----------------------------------------------------------------------------
1987 */
1988
1989 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1990                    int width, int height, Uint32 color)
1991 {
1992   int x, y;
1993
1994   for (y = src_y; y < src_y + height; y++)
1995   {
1996     for (x = src_x; x < src_x + width; x++)
1997     {
1998       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1999
2000       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
2001     }
2002   }
2003 }
2004
2005 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2006                           int src_x, int src_y, int width, int height,
2007                           int dst_x, int dst_y)
2008 {
2009   int x, y;
2010
2011   for (y = 0; y < height; y++)
2012   {
2013     for (x = 0; x < width; x++)
2014     {
2015       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2016
2017       if (pixel != BLACK_PIXEL)
2018         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2019     }
2020   }
2021 }
2022
2023
2024 /* ========================================================================= */
2025 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2026 /* (Rotozoomer) by Andreas Schiffler                                         */
2027 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
2028 /* ========================================================================= */
2029
2030 /*
2031   -----------------------------------------------------------------------------
2032   32 bit zoomer
2033
2034   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2035   -----------------------------------------------------------------------------
2036 */
2037
2038 typedef struct
2039 {
2040   Uint8 r;
2041   Uint8 g;
2042   Uint8 b;
2043   Uint8 a;
2044 } tColorRGBA;
2045
2046 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2047 {
2048   int x, y;
2049   tColorRGBA *sp, *csp, *dp;
2050   int dgap;
2051
2052   /* pointer setup */
2053   sp = csp = (tColorRGBA *) src->pixels;
2054   dp = (tColorRGBA *) dst->pixels;
2055   dgap = dst->pitch - dst->w * 4;
2056
2057   for (y = 0; y < dst->h; y++)
2058   {
2059     sp = csp;
2060
2061     for (x = 0; x < dst->w; x++)
2062     {
2063       tColorRGBA *sp0 = sp;
2064       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2065       tColorRGBA *sp00 = &sp0[0];
2066       tColorRGBA *sp01 = &sp0[1];
2067       tColorRGBA *sp10 = &sp1[0];
2068       tColorRGBA *sp11 = &sp1[1];
2069       tColorRGBA new;
2070
2071       /* create new color pixel from all four source color pixels */
2072       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2073       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2074       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2075       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2076
2077       /* draw */
2078       *dp = new;
2079
2080       /* advance source pointers */
2081       sp += 2;
2082
2083       /* advance destination pointer */
2084       dp++;
2085     }
2086
2087     /* advance source pointer */
2088     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2089
2090     /* advance destination pointers */
2091     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2092   }
2093
2094   return 0;
2095 }
2096
2097 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2098 {
2099   int x, y, *sax, *say, *csax, *csay;
2100   float sx, sy;
2101   tColorRGBA *sp, *csp, *csp0, *dp;
2102   int dgap;
2103
2104   /* use specialized zoom function when scaling down to exactly half size */
2105   if (src->w == 2 * dst->w &&
2106       src->h == 2 * dst->h)
2107     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2108
2109   /* variable setup */
2110   sx = (float) src->w / (float) dst->w;
2111   sy = (float) src->h / (float) dst->h;
2112
2113   /* allocate memory for row increments */
2114   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2115   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2116
2117   /* precalculate row increments */
2118   for (x = 0; x <= dst->w; x++)
2119     *csax++ = (int)(sx * x);
2120
2121   for (y = 0; y <= dst->h; y++)
2122     *csay++ = (int)(sy * y);
2123
2124   /* pointer setup */
2125   sp = csp = csp0 = (tColorRGBA *) src->pixels;
2126   dp = (tColorRGBA *) dst->pixels;
2127   dgap = dst->pitch - dst->w * 4;
2128
2129   csay = say;
2130   for (y = 0; y < dst->h; y++)
2131   {
2132     sp = csp;
2133     csax = sax;
2134
2135     for (x = 0; x < dst->w; x++)
2136     {
2137       /* draw */
2138       *dp = *sp;
2139
2140       /* advance source pointers */
2141       csax++;
2142       sp = csp + *csax;
2143
2144       /* advance destination pointer */
2145       dp++;
2146     }
2147
2148     /* advance source pointer */
2149     csay++;
2150     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2151
2152     /* advance destination pointers */
2153     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2154   }
2155
2156   free(sax);
2157   free(say);
2158
2159   return 0;
2160 }
2161
2162 /*
2163   -----------------------------------------------------------------------------
2164   8 bit zoomer
2165
2166   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2167   -----------------------------------------------------------------------------
2168 */
2169
2170 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2171 {
2172   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2173   Uint8 *sp, *dp, *csp;
2174   int dgap;
2175
2176   /* variable setup */
2177   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2178   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2179
2180   /* allocate memory for row increments */
2181   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2182   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2183
2184   /* precalculate row increments */
2185   csx = 0;
2186   csax = sax;
2187   for (x = 0; x < dst->w; x++)
2188   {
2189     csx += sx;
2190     *csax = (csx >> 16);
2191     csx &= 0xffff;
2192     csax++;
2193   }
2194
2195   csy = 0;
2196   csay = say;
2197   for (y = 0; y < dst->h; y++)
2198   {
2199     csy += sy;
2200     *csay = (csy >> 16);
2201     csy &= 0xffff;
2202     csay++;
2203   }
2204
2205   csx = 0;
2206   csax = sax;
2207   for (x = 0; x < dst->w; x++)
2208   {
2209     csx += (*csax);
2210     csax++;
2211   }
2212
2213   csy = 0;
2214   csay = say;
2215   for (y = 0; y < dst->h; y++)
2216   {
2217     csy += (*csay);
2218     csay++;
2219   }
2220
2221   /* pointer setup */
2222   sp = csp = (Uint8 *) src->pixels;
2223   dp = (Uint8 *) dst->pixels;
2224   dgap = dst->pitch - dst->w;
2225
2226   /* draw */
2227   csay = say;
2228   for (y = 0; y < dst->h; y++)
2229   {
2230     csax = sax;
2231     sp = csp;
2232     for (x = 0; x < dst->w; x++)
2233     {
2234       /* draw */
2235       *dp = *sp;
2236
2237       /* advance source pointers */
2238       sp += (*csax);
2239       csax++;
2240
2241       /* advance destination pointer */
2242       dp++;
2243     }
2244
2245     /* advance source pointer (for row) */
2246     csp += ((*csay) * src->pitch);
2247     csay++;
2248
2249     /* advance destination pointers */
2250     dp += dgap;
2251   }
2252
2253   free(sax);
2254   free(say);
2255
2256   return 0;
2257 }
2258
2259 /*
2260   -----------------------------------------------------------------------------
2261   zoomSurface()
2262
2263   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2264   'zoomx' and 'zoomy' are scaling factors for width and height.
2265   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2266   into a 32bit RGBA format on the fly.
2267   -----------------------------------------------------------------------------
2268 */
2269
2270 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2271 {
2272   SDL_Surface *zoom_src = NULL;
2273   SDL_Surface *zoom_dst = NULL;
2274   boolean is_converted = FALSE;
2275   boolean is_32bit;
2276   int i;
2277
2278   if (src == NULL)
2279     return NULL;
2280
2281   /* determine if source surface is 32 bit or 8 bit */
2282   is_32bit = (src->format->BitsPerPixel == 32);
2283
2284   if (is_32bit || src->format->BitsPerPixel == 8)
2285   {
2286     /* use source surface 'as is' */
2287     zoom_src = src;
2288   }
2289   else
2290   {
2291     /* new source surface is 32 bit with a defined RGB ordering */
2292     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2293                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
2294     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2295     is_32bit = TRUE;
2296     is_converted = TRUE;
2297   }
2298
2299   /* allocate surface to completely contain the zoomed surface */
2300   if (is_32bit)
2301   {
2302     /* target surface is 32 bit with source RGBA/ABGR ordering */
2303     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2304                                     zoom_src->format->Rmask,
2305                                     zoom_src->format->Gmask,
2306                                     zoom_src->format->Bmask, 0);
2307   }
2308   else
2309   {
2310     /* target surface is 8 bit */
2311     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2312                                     0, 0, 0, 0);
2313   }
2314
2315   /* lock source surface */
2316   SDL_LockSurface(zoom_src);
2317
2318   /* check which kind of surface we have */
2319   if (is_32bit)
2320   {
2321     /* call the 32 bit transformation routine to do the zooming */
2322     zoomSurfaceRGBA(zoom_src, zoom_dst);
2323   }
2324   else
2325   {
2326     /* copy palette */
2327     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2328       zoom_dst->format->palette->colors[i] =
2329         zoom_src->format->palette->colors[i];
2330     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2331
2332     /* call the 8 bit transformation routine to do the zooming */
2333     zoomSurfaceY(zoom_src, zoom_dst);
2334   }
2335
2336   /* unlock source surface */
2337   SDL_UnlockSurface(zoom_src);
2338
2339   /* free temporary surface */
2340   if (is_converted)
2341     SDL_FreeSurface(zoom_src);
2342
2343   /* return destination surface */
2344   return zoom_dst;
2345 }
2346
2347 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2348 {
2349   Bitmap *dst_bitmap = CreateBitmapStruct();
2350   SDL_Surface **dst_surface = &dst_bitmap->surface;
2351
2352   dst_width  = MAX(1, dst_width);       /* prevent zero bitmap width */
2353   dst_height = MAX(1, dst_height);      /* prevent zero bitmap height */
2354
2355   dst_bitmap->width  = dst_width;
2356   dst_bitmap->height = dst_height;
2357
2358   /* create zoomed temporary surface from source surface */
2359   *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
2360
2361   /* create native format destination surface from zoomed temporary surface */
2362   SDLSetNativeSurface(dst_surface);
2363
2364   return dst_bitmap;
2365 }
2366
2367
2368 /* ========================================================================= */
2369 /* load image to bitmap                                                      */
2370 /* ========================================================================= */
2371
2372 Bitmap *SDLLoadImage(char *filename)
2373 {
2374   Bitmap *new_bitmap = CreateBitmapStruct();
2375   SDL_Surface *sdl_image_tmp;
2376
2377   print_timestamp_init("SDLLoadImage");
2378
2379   print_timestamp_time(getBaseNamePtr(filename));
2380
2381   /* load image to temporary surface */
2382   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2383   {
2384     SetError("IMG_Load(): %s", SDL_GetError());
2385
2386     return NULL;
2387   }
2388
2389   print_timestamp_time("IMG_Load");
2390
2391   UPDATE_BUSY_STATE();
2392
2393   /* create native non-transparent surface for current image */
2394   if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2395   {
2396     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2397
2398     return NULL;
2399   }
2400
2401   print_timestamp_time("SDL_DisplayFormat (opaque)");
2402
2403   UPDATE_BUSY_STATE();
2404
2405   /* create native transparent surface for current image */
2406   SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2407                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2408
2409   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2410   {
2411     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2412
2413     return NULL;
2414   }
2415
2416   print_timestamp_time("SDL_DisplayFormat (masked)");
2417
2418   UPDATE_BUSY_STATE();
2419
2420   /* free temporary surface */
2421   SDL_FreeSurface(sdl_image_tmp);
2422
2423   new_bitmap->width = new_bitmap->surface->w;
2424   new_bitmap->height = new_bitmap->surface->h;
2425
2426   print_timestamp_done("SDLLoadImage");
2427
2428   return new_bitmap;
2429 }
2430
2431
2432 /* ------------------------------------------------------------------------- */
2433 /* custom cursor fuctions                                                    */
2434 /* ------------------------------------------------------------------------- */
2435
2436 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2437 {
2438   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2439                           cursor_info->width, cursor_info->height,
2440                           cursor_info->hot_x, cursor_info->hot_y);
2441 }
2442
2443 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2444 {
2445   static struct MouseCursorInfo *last_cursor_info = NULL;
2446   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2447   static SDL_Cursor *cursor_default = NULL;
2448   static SDL_Cursor *cursor_current = NULL;
2449
2450   /* if invoked for the first time, store the SDL default cursor */
2451   if (cursor_default == NULL)
2452     cursor_default = SDL_GetCursor();
2453
2454   /* only create new cursor if cursor info (custom only) has changed */
2455   if (cursor_info != NULL && cursor_info != last_cursor_info)
2456   {
2457     cursor_current = create_cursor(cursor_info);
2458     last_cursor_info = cursor_info;
2459   }
2460
2461   /* only set new cursor if cursor info (custom or NULL) has changed */
2462   if (cursor_info != last_cursor_info2)
2463     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2464
2465   last_cursor_info2 = cursor_info;
2466 }
2467
2468
2469 /* ========================================================================= */
2470 /* audio functions                                                           */
2471 /* ========================================================================= */
2472
2473 void SDLOpenAudio(void)
2474 {
2475 #if !defined(TARGET_SDL2)
2476   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2477     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2478 #endif
2479
2480   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2481   {
2482     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2483     return;
2484   }
2485
2486   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2487                     AUDIO_NUM_CHANNELS_STEREO,
2488                     setup.system.audio_fragment_size) < 0)
2489   {
2490     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2491     return;
2492   }
2493
2494   audio.sound_available = TRUE;
2495   audio.music_available = TRUE;
2496   audio.loops_available = TRUE;
2497   audio.sound_enabled = TRUE;
2498
2499   /* set number of available mixer channels */
2500   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2501   audio.music_channel = MUSIC_CHANNEL;
2502   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2503
2504   Mixer_InitChannels();
2505 }
2506
2507 void SDLCloseAudio(void)
2508 {
2509   Mix_HaltMusic();
2510   Mix_HaltChannel(-1);
2511
2512   Mix_CloseAudio();
2513   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2514 }
2515
2516
2517 /* ========================================================================= */
2518 /* event functions                                                           */
2519 /* ========================================================================= */
2520
2521 void SDLNextEvent(Event *event)
2522 {
2523   SDL_WaitEvent(event);
2524
2525   if (event->type == EVENT_BUTTONPRESS ||
2526       event->type == EVENT_BUTTONRELEASE)
2527   {
2528     if (((ButtonEvent *)event)->x > video_xoffset)
2529       ((ButtonEvent *)event)->x -= video_xoffset;
2530     else
2531       ((ButtonEvent *)event)->x = 0;
2532     if (((ButtonEvent *)event)->y > video_yoffset)
2533       ((ButtonEvent *)event)->y -= video_yoffset;
2534     else
2535       ((ButtonEvent *)event)->y = 0;
2536   }
2537   else if (event->type == EVENT_MOTIONNOTIFY)
2538   {
2539     if (((MotionEvent *)event)->x > video_xoffset)
2540       ((MotionEvent *)event)->x -= video_xoffset;
2541     else
2542       ((MotionEvent *)event)->x = 0;
2543     if (((MotionEvent *)event)->y > video_yoffset)
2544       ((MotionEvent *)event)->y -= video_yoffset;
2545     else
2546       ((MotionEvent *)event)->y = 0;
2547   }
2548 }
2549
2550 void SDLHandleWindowManagerEvent(Event *event)
2551 {
2552 #ifdef DEBUG
2553 #if defined(PLATFORM_WIN32)
2554   // experimental drag and drop code
2555
2556   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2557   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2558
2559 #if defined(TARGET_SDL2)
2560   if (syswmmsg->msg.win.msg == WM_DROPFILES)
2561 #else
2562   if (syswmmsg->msg == WM_DROPFILES)
2563 #endif
2564   {
2565 #if defined(TARGET_SDL2)
2566     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2567 #else
2568     HDROP hdrop = (HDROP)syswmmsg->wParam;
2569 #endif
2570     int i, num_files;
2571
2572     printf("::: SDL_SYSWMEVENT:\n");
2573
2574     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2575
2576     for (i = 0; i < num_files; i++)
2577     {
2578       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2579       char buffer[buffer_len + 1];
2580
2581       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2582
2583       printf("::: - '%s'\n", buffer);
2584     }
2585
2586 #if defined(TARGET_SDL2)
2587     DragFinish((HDROP)syswmmsg->msg.win.wParam);
2588 #else
2589     DragFinish((HDROP)syswmmsg->wParam);
2590 #endif
2591   }
2592 #endif
2593 #endif
2594 }
2595
2596
2597 /* ========================================================================= */
2598 /* joystick functions                                                        */
2599 /* ========================================================================= */
2600
2601 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2602 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2603 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2604
2605 static boolean SDLOpenJoystick(int nr)
2606 {
2607   if (nr < 0 || nr > MAX_PLAYERS)
2608     return FALSE;
2609
2610   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2611 }
2612
2613 static void SDLCloseJoystick(int nr)
2614 {
2615   if (nr < 0 || nr > MAX_PLAYERS)
2616     return;
2617
2618   SDL_JoystickClose(sdl_joystick[nr]);
2619
2620   sdl_joystick[nr] = NULL;
2621 }
2622
2623 static boolean SDLCheckJoystickOpened(int nr)
2624 {
2625   if (nr < 0 || nr > MAX_PLAYERS)
2626     return FALSE;
2627
2628 #if defined(TARGET_SDL2)
2629   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2630 #else
2631   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2632 #endif
2633 }
2634
2635 void HandleJoystickEvent(Event *event)
2636 {
2637   switch(event->type)
2638   {
2639     case SDL_JOYAXISMOTION:
2640       if (event->jaxis.axis < 2)
2641         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2642       break;
2643
2644     case SDL_JOYBUTTONDOWN:
2645       if (event->jbutton.button < 2)
2646         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2647       break;
2648
2649     case SDL_JOYBUTTONUP:
2650       if (event->jbutton.button < 2)
2651         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2652       break;
2653
2654     default:
2655       break;
2656   }
2657 }
2658
2659 void SDLInitJoysticks()
2660 {
2661   static boolean sdl_joystick_subsystem_initialized = FALSE;
2662   boolean print_warning = !sdl_joystick_subsystem_initialized;
2663   int i;
2664
2665   if (!sdl_joystick_subsystem_initialized)
2666   {
2667     sdl_joystick_subsystem_initialized = TRUE;
2668
2669     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2670     {
2671       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2672       return;
2673     }
2674   }
2675
2676   for (i = 0; i < MAX_PLAYERS; i++)
2677   {
2678     /* get configured joystick for this player */
2679     char *device_name = setup.input[i].joy.device_name;
2680     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2681
2682     if (joystick_nr >= SDL_NumJoysticks())
2683     {
2684       if (setup.input[i].use_joystick && print_warning)
2685         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2686
2687       joystick_nr = -1;
2688     }
2689
2690     /* misuse joystick file descriptor variable to store joystick number */
2691     joystick.fd[i] = joystick_nr;
2692
2693     if (joystick_nr == -1)
2694       continue;
2695
2696     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2697     if (SDLCheckJoystickOpened(joystick_nr))
2698       SDLCloseJoystick(joystick_nr);
2699
2700     if (!setup.input[i].use_joystick)
2701       continue;
2702
2703     if (!SDLOpenJoystick(joystick_nr))
2704     {
2705       if (print_warning)
2706         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2707
2708       continue;
2709     }
2710
2711     joystick.status = JOYSTICK_ACTIVATED;
2712   }
2713 }
2714
2715 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2716 {
2717   if (nr < 0 || nr >= MAX_PLAYERS)
2718     return FALSE;
2719
2720   if (x != NULL)
2721     *x = sdl_js_axis[nr][0];
2722   if (y != NULL)
2723     *y = sdl_js_axis[nr][1];
2724
2725   if (b1 != NULL)
2726     *b1 = sdl_js_button[nr][0];
2727   if (b2 != NULL)
2728     *b2 = sdl_js_button[nr][1];
2729
2730   return TRUE;
2731 }