added drawing global animations during post delay of screen fading
[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   if (post_delay > 0)
1400   {
1401     unsigned int time_post_delay;
1402
1403     time_current = SDL_GetTicks();
1404     time_post_delay = time_current + post_delay;
1405
1406     while (time_current < time_post_delay)
1407     {
1408       // do not wait longer than 10 ms at a time to be able to ...
1409       Delay(MIN(10, time_post_delay - time_current));
1410
1411       // ... continue drawing global animations during post delay
1412       UpdateScreen(NULL);
1413
1414       time_current = SDL_GetTicks();
1415     }
1416   }
1417 }
1418
1419 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1420                        int to_x, int to_y, Uint32 color)
1421 {
1422   SDL_Surface *surface = dst_bitmap->surface;
1423   SDL_Rect rect;
1424
1425   if (from_x > to_x)
1426     swap_numbers(&from_x, &to_x);
1427
1428   if (from_y > to_y)
1429     swap_numbers(&from_y, &to_y);
1430
1431   rect.x = from_x;
1432   rect.y = from_y;
1433   rect.w = (to_x - from_x + 1);
1434   rect.h = (to_y - from_y + 1);
1435
1436   if (dst_bitmap == backbuffer || dst_bitmap == window)
1437   {
1438     rect.x += video_xoffset;
1439     rect.y += video_yoffset;
1440   }
1441
1442   SDL_FillRect(surface, &rect, color);
1443 }
1444
1445 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1446                  int to_x, int to_y, Uint32 color)
1447 {
1448   if (dst_bitmap == backbuffer || dst_bitmap == window)
1449   {
1450     from_x += video_xoffset;
1451     from_y += video_yoffset;
1452     to_x += video_xoffset;
1453     to_y += video_yoffset;
1454   }
1455
1456   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1457 }
1458
1459 #if ENABLE_UNUSED_CODE
1460 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1461                   int num_points, Uint32 color)
1462 {
1463   int i, x, y;
1464   int line_width = 4;
1465
1466   for (i = 0; i < num_points - 1; i++)
1467   {
1468     for (x = 0; x < line_width; x++)
1469     {
1470       for (y = 0; y < line_width; y++)
1471       {
1472         int dx = x - line_width / 2;
1473         int dy = y - line_width / 2;
1474
1475         if ((x == 0 && y == 0) ||
1476             (x == 0 && y == line_width - 1) ||
1477             (x == line_width - 1 && y == 0) ||
1478             (x == line_width - 1 && y == line_width - 1))
1479           continue;
1480
1481         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1482                  points[i+1].x + dx, points[i+1].y + dy, color);
1483       }
1484     }
1485   }
1486 }
1487 #endif
1488
1489 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1490 {
1491   SDL_Surface *surface = src_bitmap->surface;
1492
1493   if (src_bitmap == backbuffer || src_bitmap == window)
1494   {
1495     x += video_xoffset;
1496     y += video_yoffset;
1497   }
1498
1499   switch (surface->format->BytesPerPixel)
1500   {
1501     case 1:             /* assuming 8-bpp */
1502     {
1503       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1504     }
1505     break;
1506
1507     case 2:             /* probably 15-bpp or 16-bpp */
1508     {
1509       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1510     }
1511     break;
1512
1513   case 3:               /* slow 24-bpp mode; usually not used */
1514     {
1515       /* does this work? */
1516       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1517       Uint32 color = 0;
1518       int shift;
1519
1520       shift = surface->format->Rshift;
1521       color |= *(pix + shift / 8) >> shift;
1522       shift = surface->format->Gshift;
1523       color |= *(pix + shift / 8) >> shift;
1524       shift = surface->format->Bshift;
1525       color |= *(pix + shift / 8) >> shift;
1526
1527       return color;
1528     }
1529     break;
1530
1531   case 4:               /* probably 32-bpp */
1532     {
1533       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1534     }
1535     break;
1536   }
1537
1538   return 0;
1539 }
1540
1541
1542 /* ========================================================================= */
1543 /* The following functions were taken from the SGE library                   */
1544 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1545 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1546 /* ========================================================================= */
1547
1548 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1549 {
1550   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1551   {
1552     switch (surface->format->BytesPerPixel)
1553     {
1554       case 1:
1555       {
1556         /* Assuming 8-bpp */
1557         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1558       }
1559       break;
1560
1561       case 2:
1562       {
1563         /* Probably 15-bpp or 16-bpp */
1564         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1565       }
1566       break;
1567
1568       case 3:
1569       {
1570         /* Slow 24-bpp mode, usually not used */
1571         Uint8 *pix;
1572         int shift;
1573
1574         /* Gack - slow, but endian correct */
1575         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1576         shift = surface->format->Rshift;
1577         *(pix+shift/8) = color>>shift;
1578         shift = surface->format->Gshift;
1579         *(pix+shift/8) = color>>shift;
1580         shift = surface->format->Bshift;
1581         *(pix+shift/8) = color>>shift;
1582       }
1583       break;
1584
1585       case 4:
1586       {
1587         /* Probably 32-bpp */
1588         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1589       }
1590       break;
1591     }
1592   }
1593 }
1594
1595 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1596                   Uint8 R, Uint8 G, Uint8 B)
1597 {
1598   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1599 }
1600
1601 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1602 {
1603   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1604 }
1605
1606 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1607 {
1608   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1609 }
1610
1611 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1612 {
1613   Uint8 *pix;
1614   int shift;
1615
1616   /* Gack - slow, but endian correct */
1617   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1618   shift = surface->format->Rshift;
1619   *(pix+shift/8) = color>>shift;
1620   shift = surface->format->Gshift;
1621   *(pix+shift/8) = color>>shift;
1622   shift = surface->format->Bshift;
1623   *(pix+shift/8) = color>>shift;
1624 }
1625
1626 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1627 {
1628   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1629 }
1630
1631 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1632 {
1633   switch (dest->format->BytesPerPixel)
1634   {
1635     case 1:
1636       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1637       break;
1638
1639     case 2:
1640       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1641       break;
1642
1643     case 3:
1644       _PutPixel24(dest,x,y,color);
1645       break;
1646
1647     case 4:
1648       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1649       break;
1650   }
1651 }
1652
1653 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1654 {
1655   if (SDL_MUSTLOCK(surface))
1656   {
1657     if (SDL_LockSurface(surface) < 0)
1658     {
1659       return;
1660     }
1661   }
1662
1663   _PutPixel(surface, x, y, color);
1664
1665   if (SDL_MUSTLOCK(surface))
1666   {
1667     SDL_UnlockSurface(surface);
1668   }
1669 }
1670
1671 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1672                   Uint8 r, Uint8 g, Uint8 b)
1673 {
1674   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1675 }
1676
1677 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1678 {
1679   if (y >= 0 && y <= dest->h - 1)
1680   {
1681     switch (dest->format->BytesPerPixel)
1682     {
1683       case 1:
1684         return y*dest->pitch;
1685         break;
1686
1687       case 2:
1688         return y*dest->pitch/2;
1689         break;
1690
1691       case 3:
1692         return y*dest->pitch;
1693         break;
1694
1695       case 4:
1696         return y*dest->pitch/4;
1697         break;
1698     }
1699   }
1700
1701   return -1;
1702 }
1703
1704 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1705 {
1706   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1707   {
1708     switch (surface->format->BytesPerPixel)
1709     {
1710       case 1:
1711       {
1712         /* Assuming 8-bpp */
1713         *((Uint8 *)surface->pixels + ypitch + x) = color;
1714       }
1715       break;
1716
1717       case 2:
1718       {
1719         /* Probably 15-bpp or 16-bpp */
1720         *((Uint16 *)surface->pixels + ypitch + x) = color;
1721       }
1722       break;
1723
1724       case 3:
1725       {
1726         /* Slow 24-bpp mode, usually not used */
1727         Uint8 *pix;
1728         int shift;
1729
1730         /* Gack - slow, but endian correct */
1731         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1732         shift = surface->format->Rshift;
1733         *(pix+shift/8) = color>>shift;
1734         shift = surface->format->Gshift;
1735         *(pix+shift/8) = color>>shift;
1736         shift = surface->format->Bshift;
1737         *(pix+shift/8) = color>>shift;
1738       }
1739       break;
1740
1741       case 4:
1742       {
1743         /* Probably 32-bpp */
1744         *((Uint32 *)surface->pixels + ypitch + x) = color;
1745       }
1746       break;
1747     }
1748   }
1749 }
1750
1751 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1752                Uint32 Color)
1753 {
1754   SDL_Rect l;
1755
1756   if (SDL_MUSTLOCK(Surface))
1757   {
1758     if (SDL_LockSurface(Surface) < 0)
1759     {
1760       return;
1761     }
1762   }
1763
1764   if (x1 > x2)
1765   {
1766     Sint16 tmp = x1;
1767     x1 = x2;
1768     x2 = tmp;
1769   }
1770
1771   /* Do the clipping */
1772   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1773     return;
1774   if (x1 < 0)
1775     x1 = 0;
1776   if (x2 > Surface->w - 1)
1777     x2 = Surface->w - 1;
1778
1779   l.x = x1;
1780   l.y = y;
1781   l.w = x2 - x1 + 1;
1782   l.h = 1;
1783
1784   SDL_FillRect(Surface, &l, Color);
1785
1786   if (SDL_MUSTLOCK(Surface))
1787   {
1788     SDL_UnlockSurface(Surface);
1789   }
1790 }
1791
1792 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1793                   Uint8 R, Uint8 G, Uint8 B)
1794 {
1795   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1796 }
1797
1798 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1799 {
1800   SDL_Rect l;
1801
1802   if (x1 > x2)
1803   {
1804     Sint16 tmp = x1;
1805     x1 = x2;
1806     x2 = tmp;
1807   }
1808
1809   /* Do the clipping */
1810   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1811     return;
1812   if (x1 < 0)
1813     x1 = 0;
1814   if (x2 > Surface->w - 1)
1815     x2 = Surface->w - 1;
1816
1817   l.x = x1;
1818   l.y = y;
1819   l.w = x2 - x1 + 1;
1820   l.h = 1;
1821
1822   SDL_FillRect(Surface, &l, Color);
1823 }
1824
1825 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1826                Uint32 Color)
1827 {
1828   SDL_Rect l;
1829
1830   if (SDL_MUSTLOCK(Surface))
1831   {
1832     if (SDL_LockSurface(Surface) < 0)
1833     {
1834       return;
1835     }
1836   }
1837
1838   if (y1 > y2)
1839   {
1840     Sint16 tmp = y1;
1841     y1 = y2;
1842     y2 = tmp;
1843   }
1844
1845   /* Do the clipping */
1846   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1847     return;
1848   if (y1 < 0)
1849     y1 = 0;
1850   if (y2 > Surface->h - 1)
1851     y2 = Surface->h - 1;
1852
1853   l.x = x;
1854   l.y = y1;
1855   l.w = 1;
1856   l.h = y2 - y1 + 1;
1857
1858   SDL_FillRect(Surface, &l, Color);
1859
1860   if (SDL_MUSTLOCK(Surface))
1861   {
1862     SDL_UnlockSurface(Surface);
1863   }
1864 }
1865
1866 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1867                   Uint8 R, Uint8 G, Uint8 B)
1868 {
1869   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1870 }
1871
1872 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1873 {
1874   SDL_Rect l;
1875
1876   if (y1 > y2)
1877   {
1878     Sint16 tmp = y1;
1879     y1 = y2;
1880     y2 = tmp;
1881   }
1882
1883   /* Do the clipping */
1884   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1885     return;
1886   if (y1 < 0)
1887     y1 = 0;
1888   if (y2 > Surface->h - 1)
1889     y2 = Surface->h - 1;
1890
1891   l.x = x;
1892   l.y = y1;
1893   l.w = 1;
1894   l.h = y2 - y1 + 1;
1895
1896   SDL_FillRect(Surface, &l, Color);
1897 }
1898
1899 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1900                 Sint16 x2, Sint16 y2, Uint32 Color,
1901                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1902                               Uint32 Color))
1903 {
1904   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1905
1906   dx = x2 - x1;
1907   dy = y2 - y1;
1908
1909   sdx = (dx < 0) ? -1 : 1;
1910   sdy = (dy < 0) ? -1 : 1;
1911
1912   dx = sdx * dx + 1;
1913   dy = sdy * dy + 1;
1914
1915   x = y = 0;
1916
1917   px = x1;
1918   py = y1;
1919
1920   if (dx >= dy)
1921   {
1922     for (x = 0; x < dx; x++)
1923     {
1924       Callback(Surface, px, py, Color);
1925
1926       y += dy;
1927       if (y >= dx)
1928       {
1929         y -= dx;
1930         py += sdy;
1931       }
1932
1933       px += sdx;
1934     }
1935   }
1936   else
1937   {
1938     for (y = 0; y < dy; y++)
1939     {
1940       Callback(Surface, px, py, Color);
1941
1942       x += dx;
1943       if (x >= dy)
1944       {
1945         x -= dy;
1946         px += sdx;
1947       }
1948
1949       py += sdy;
1950     }
1951   }
1952 }
1953
1954 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1955                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1956                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1957                                  Uint32 Color))
1958 {
1959   sge_DoLine(Surface, X1, Y1, X2, Y2,
1960              SDL_MapRGB(Surface->format, R, G, B), Callback);
1961 }
1962
1963 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1964               Uint32 Color)
1965 {
1966   if (SDL_MUSTLOCK(Surface))
1967   {
1968     if (SDL_LockSurface(Surface) < 0)
1969       return;
1970    }
1971
1972    /* Draw the line */
1973    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1974
1975    /* unlock the display */
1976    if (SDL_MUSTLOCK(Surface))
1977    {
1978       SDL_UnlockSurface(Surface);
1979    }
1980 }
1981
1982 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1983                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1984 {
1985   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1986 }
1987
1988 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1989 {
1990   if (dst_bitmap == backbuffer || dst_bitmap == window)
1991   {
1992     x += video_xoffset;
1993     y += video_yoffset;
1994   }
1995
1996   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1997 }
1998
1999
2000 /*
2001   -----------------------------------------------------------------------------
2002   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
2003   -----------------------------------------------------------------------------
2004 */
2005
2006 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
2007                    int width, int height, Uint32 color)
2008 {
2009   int x, y;
2010
2011   for (y = src_y; y < src_y + height; y++)
2012   {
2013     for (x = src_x; x < src_x + width; x++)
2014     {
2015       Uint32 pixel = SDLGetPixel(bitmap, x, y);
2016
2017       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
2018     }
2019   }
2020 }
2021
2022 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
2023                           int src_x, int src_y, int width, int height,
2024                           int dst_x, int dst_y)
2025 {
2026   int x, y;
2027
2028   for (y = 0; y < height; y++)
2029   {
2030     for (x = 0; x < width; x++)
2031     {
2032       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
2033
2034       if (pixel != BLACK_PIXEL)
2035         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
2036     }
2037   }
2038 }
2039
2040
2041 /* ========================================================================= */
2042 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
2043 /* (Rotozoomer) by Andreas Schiffler                                         */
2044 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
2045 /* ========================================================================= */
2046
2047 /*
2048   -----------------------------------------------------------------------------
2049   32 bit zoomer
2050
2051   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
2052   -----------------------------------------------------------------------------
2053 */
2054
2055 typedef struct
2056 {
2057   Uint8 r;
2058   Uint8 g;
2059   Uint8 b;
2060   Uint8 a;
2061 } tColorRGBA;
2062
2063 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
2064 {
2065   int x, y;
2066   tColorRGBA *sp, *csp, *dp;
2067   int dgap;
2068
2069   /* pointer setup */
2070   sp = csp = (tColorRGBA *) src->pixels;
2071   dp = (tColorRGBA *) dst->pixels;
2072   dgap = dst->pitch - dst->w * 4;
2073
2074   for (y = 0; y < dst->h; y++)
2075   {
2076     sp = csp;
2077
2078     for (x = 0; x < dst->w; x++)
2079     {
2080       tColorRGBA *sp0 = sp;
2081       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
2082       tColorRGBA *sp00 = &sp0[0];
2083       tColorRGBA *sp01 = &sp0[1];
2084       tColorRGBA *sp10 = &sp1[0];
2085       tColorRGBA *sp11 = &sp1[1];
2086       tColorRGBA new;
2087
2088       /* create new color pixel from all four source color pixels */
2089       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
2090       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
2091       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
2092       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
2093
2094       /* draw */
2095       *dp = new;
2096
2097       /* advance source pointers */
2098       sp += 2;
2099
2100       /* advance destination pointer */
2101       dp++;
2102     }
2103
2104     /* advance source pointer */
2105     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
2106
2107     /* advance destination pointers */
2108     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2109   }
2110
2111   return 0;
2112 }
2113
2114 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
2115 {
2116   int x, y, *sax, *say, *csax, *csay;
2117   float sx, sy;
2118   tColorRGBA *sp, *csp, *csp0, *dp;
2119   int dgap;
2120
2121   /* use specialized zoom function when scaling down to exactly half size */
2122   if (src->w == 2 * dst->w &&
2123       src->h == 2 * dst->h)
2124     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
2125
2126   /* variable setup */
2127   sx = (float) src->w / (float) dst->w;
2128   sy = (float) src->h / (float) dst->h;
2129
2130   /* allocate memory for row increments */
2131   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
2132   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
2133
2134   /* precalculate row increments */
2135   for (x = 0; x <= dst->w; x++)
2136     *csax++ = (int)(sx * x);
2137
2138   for (y = 0; y <= dst->h; y++)
2139     *csay++ = (int)(sy * y);
2140
2141   /* pointer setup */
2142   sp = csp = csp0 = (tColorRGBA *) src->pixels;
2143   dp = (tColorRGBA *) dst->pixels;
2144   dgap = dst->pitch - dst->w * 4;
2145
2146   csay = say;
2147   for (y = 0; y < dst->h; y++)
2148   {
2149     sp = csp;
2150     csax = sax;
2151
2152     for (x = 0; x < dst->w; x++)
2153     {
2154       /* draw */
2155       *dp = *sp;
2156
2157       /* advance source pointers */
2158       csax++;
2159       sp = csp + *csax;
2160
2161       /* advance destination pointer */
2162       dp++;
2163     }
2164
2165     /* advance source pointer */
2166     csay++;
2167     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2168
2169     /* advance destination pointers */
2170     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2171   }
2172
2173   free(sax);
2174   free(say);
2175
2176   return 0;
2177 }
2178
2179 /*
2180   -----------------------------------------------------------------------------
2181   8 bit zoomer
2182
2183   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2184   -----------------------------------------------------------------------------
2185 */
2186
2187 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2188 {
2189   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2190   Uint8 *sp, *dp, *csp;
2191   int dgap;
2192
2193   /* variable setup */
2194   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2195   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2196
2197   /* allocate memory for row increments */
2198   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2199   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2200
2201   /* precalculate row increments */
2202   csx = 0;
2203   csax = sax;
2204   for (x = 0; x < dst->w; x++)
2205   {
2206     csx += sx;
2207     *csax = (csx >> 16);
2208     csx &= 0xffff;
2209     csax++;
2210   }
2211
2212   csy = 0;
2213   csay = say;
2214   for (y = 0; y < dst->h; y++)
2215   {
2216     csy += sy;
2217     *csay = (csy >> 16);
2218     csy &= 0xffff;
2219     csay++;
2220   }
2221
2222   csx = 0;
2223   csax = sax;
2224   for (x = 0; x < dst->w; x++)
2225   {
2226     csx += (*csax);
2227     csax++;
2228   }
2229
2230   csy = 0;
2231   csay = say;
2232   for (y = 0; y < dst->h; y++)
2233   {
2234     csy += (*csay);
2235     csay++;
2236   }
2237
2238   /* pointer setup */
2239   sp = csp = (Uint8 *) src->pixels;
2240   dp = (Uint8 *) dst->pixels;
2241   dgap = dst->pitch - dst->w;
2242
2243   /* draw */
2244   csay = say;
2245   for (y = 0; y < dst->h; y++)
2246   {
2247     csax = sax;
2248     sp = csp;
2249     for (x = 0; x < dst->w; x++)
2250     {
2251       /* draw */
2252       *dp = *sp;
2253
2254       /* advance source pointers */
2255       sp += (*csax);
2256       csax++;
2257
2258       /* advance destination pointer */
2259       dp++;
2260     }
2261
2262     /* advance source pointer (for row) */
2263     csp += ((*csay) * src->pitch);
2264     csay++;
2265
2266     /* advance destination pointers */
2267     dp += dgap;
2268   }
2269
2270   free(sax);
2271   free(say);
2272
2273   return 0;
2274 }
2275
2276 /*
2277   -----------------------------------------------------------------------------
2278   zoomSurface()
2279
2280   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2281   'zoomx' and 'zoomy' are scaling factors for width and height.
2282   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2283   into a 32bit RGBA format on the fly.
2284   -----------------------------------------------------------------------------
2285 */
2286
2287 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2288 {
2289   SDL_Surface *zoom_src = NULL;
2290   SDL_Surface *zoom_dst = NULL;
2291   boolean is_converted = FALSE;
2292   boolean is_32bit;
2293   int i;
2294
2295   if (src == NULL)
2296     return NULL;
2297
2298   /* determine if source surface is 32 bit or 8 bit */
2299   is_32bit = (src->format->BitsPerPixel == 32);
2300
2301   if (is_32bit || src->format->BitsPerPixel == 8)
2302   {
2303     /* use source surface 'as is' */
2304     zoom_src = src;
2305   }
2306   else
2307   {
2308     /* new source surface is 32 bit with a defined RGB ordering */
2309     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2310                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
2311     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2312     is_32bit = TRUE;
2313     is_converted = TRUE;
2314   }
2315
2316   /* allocate surface to completely contain the zoomed surface */
2317   if (is_32bit)
2318   {
2319     /* target surface is 32 bit with source RGBA/ABGR ordering */
2320     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2321                                     zoom_src->format->Rmask,
2322                                     zoom_src->format->Gmask,
2323                                     zoom_src->format->Bmask, 0);
2324   }
2325   else
2326   {
2327     /* target surface is 8 bit */
2328     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2329                                     0, 0, 0, 0);
2330   }
2331
2332   /* lock source surface */
2333   SDL_LockSurface(zoom_src);
2334
2335   /* check which kind of surface we have */
2336   if (is_32bit)
2337   {
2338     /* call the 32 bit transformation routine to do the zooming */
2339     zoomSurfaceRGBA(zoom_src, zoom_dst);
2340   }
2341   else
2342   {
2343     /* copy palette */
2344     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2345       zoom_dst->format->palette->colors[i] =
2346         zoom_src->format->palette->colors[i];
2347     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2348
2349     /* call the 8 bit transformation routine to do the zooming */
2350     zoomSurfaceY(zoom_src, zoom_dst);
2351   }
2352
2353   /* unlock source surface */
2354   SDL_UnlockSurface(zoom_src);
2355
2356   /* free temporary surface */
2357   if (is_converted)
2358     SDL_FreeSurface(zoom_src);
2359
2360   /* return destination surface */
2361   return zoom_dst;
2362 }
2363
2364 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2365 {
2366   Bitmap *dst_bitmap = CreateBitmapStruct();
2367   SDL_Surface **dst_surface = &dst_bitmap->surface;
2368
2369   dst_width  = MAX(1, dst_width);       /* prevent zero bitmap width */
2370   dst_height = MAX(1, dst_height);      /* prevent zero bitmap height */
2371
2372   dst_bitmap->width  = dst_width;
2373   dst_bitmap->height = dst_height;
2374
2375   /* create zoomed temporary surface from source surface */
2376   *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
2377
2378   /* create native format destination surface from zoomed temporary surface */
2379   SDLSetNativeSurface(dst_surface);
2380
2381   return dst_bitmap;
2382 }
2383
2384
2385 /* ========================================================================= */
2386 /* load image to bitmap                                                      */
2387 /* ========================================================================= */
2388
2389 Bitmap *SDLLoadImage(char *filename)
2390 {
2391   Bitmap *new_bitmap = CreateBitmapStruct();
2392   SDL_Surface *sdl_image_tmp;
2393
2394   print_timestamp_init("SDLLoadImage");
2395
2396   print_timestamp_time(getBaseNamePtr(filename));
2397
2398   /* load image to temporary surface */
2399   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2400   {
2401     SetError("IMG_Load(): %s", SDL_GetError());
2402
2403     return NULL;
2404   }
2405
2406   print_timestamp_time("IMG_Load");
2407
2408   UPDATE_BUSY_STATE();
2409
2410   /* create native non-transparent surface for current image */
2411   if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2412   {
2413     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2414
2415     return NULL;
2416   }
2417
2418   print_timestamp_time("SDL_DisplayFormat (opaque)");
2419
2420   UPDATE_BUSY_STATE();
2421
2422   /* create native transparent surface for current image */
2423   SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2424                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2425
2426   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2427   {
2428     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2429
2430     return NULL;
2431   }
2432
2433   print_timestamp_time("SDL_DisplayFormat (masked)");
2434
2435   UPDATE_BUSY_STATE();
2436
2437   /* free temporary surface */
2438   SDL_FreeSurface(sdl_image_tmp);
2439
2440   new_bitmap->width = new_bitmap->surface->w;
2441   new_bitmap->height = new_bitmap->surface->h;
2442
2443   print_timestamp_done("SDLLoadImage");
2444
2445   return new_bitmap;
2446 }
2447
2448
2449 /* ------------------------------------------------------------------------- */
2450 /* custom cursor fuctions                                                    */
2451 /* ------------------------------------------------------------------------- */
2452
2453 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2454 {
2455   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2456                           cursor_info->width, cursor_info->height,
2457                           cursor_info->hot_x, cursor_info->hot_y);
2458 }
2459
2460 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2461 {
2462   static struct MouseCursorInfo *last_cursor_info = NULL;
2463   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2464   static SDL_Cursor *cursor_default = NULL;
2465   static SDL_Cursor *cursor_current = NULL;
2466
2467   /* if invoked for the first time, store the SDL default cursor */
2468   if (cursor_default == NULL)
2469     cursor_default = SDL_GetCursor();
2470
2471   /* only create new cursor if cursor info (custom only) has changed */
2472   if (cursor_info != NULL && cursor_info != last_cursor_info)
2473   {
2474     cursor_current = create_cursor(cursor_info);
2475     last_cursor_info = cursor_info;
2476   }
2477
2478   /* only set new cursor if cursor info (custom or NULL) has changed */
2479   if (cursor_info != last_cursor_info2)
2480     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2481
2482   last_cursor_info2 = cursor_info;
2483 }
2484
2485
2486 /* ========================================================================= */
2487 /* audio functions                                                           */
2488 /* ========================================================================= */
2489
2490 void SDLOpenAudio(void)
2491 {
2492 #if !defined(TARGET_SDL2)
2493   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2494     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2495 #endif
2496
2497   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2498   {
2499     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2500     return;
2501   }
2502
2503   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2504                     AUDIO_NUM_CHANNELS_STEREO,
2505                     setup.system.audio_fragment_size) < 0)
2506   {
2507     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2508     return;
2509   }
2510
2511   audio.sound_available = TRUE;
2512   audio.music_available = TRUE;
2513   audio.loops_available = TRUE;
2514   audio.sound_enabled = TRUE;
2515
2516   /* set number of available mixer channels */
2517   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2518   audio.music_channel = MUSIC_CHANNEL;
2519   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2520
2521   Mixer_InitChannels();
2522 }
2523
2524 void SDLCloseAudio(void)
2525 {
2526   Mix_HaltMusic();
2527   Mix_HaltChannel(-1);
2528
2529   Mix_CloseAudio();
2530   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2531 }
2532
2533
2534 /* ========================================================================= */
2535 /* event functions                                                           */
2536 /* ========================================================================= */
2537
2538 void SDLNextEvent(Event *event)
2539 {
2540   SDL_WaitEvent(event);
2541
2542   if (event->type == EVENT_BUTTONPRESS ||
2543       event->type == EVENT_BUTTONRELEASE)
2544   {
2545     if (((ButtonEvent *)event)->x > video_xoffset)
2546       ((ButtonEvent *)event)->x -= video_xoffset;
2547     else
2548       ((ButtonEvent *)event)->x = 0;
2549     if (((ButtonEvent *)event)->y > video_yoffset)
2550       ((ButtonEvent *)event)->y -= video_yoffset;
2551     else
2552       ((ButtonEvent *)event)->y = 0;
2553   }
2554   else if (event->type == EVENT_MOTIONNOTIFY)
2555   {
2556     if (((MotionEvent *)event)->x > video_xoffset)
2557       ((MotionEvent *)event)->x -= video_xoffset;
2558     else
2559       ((MotionEvent *)event)->x = 0;
2560     if (((MotionEvent *)event)->y > video_yoffset)
2561       ((MotionEvent *)event)->y -= video_yoffset;
2562     else
2563       ((MotionEvent *)event)->y = 0;
2564   }
2565 }
2566
2567 void SDLHandleWindowManagerEvent(Event *event)
2568 {
2569 #ifdef DEBUG
2570 #if defined(PLATFORM_WIN32)
2571   // experimental drag and drop code
2572
2573   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2574   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2575
2576 #if defined(TARGET_SDL2)
2577   if (syswmmsg->msg.win.msg == WM_DROPFILES)
2578 #else
2579   if (syswmmsg->msg == WM_DROPFILES)
2580 #endif
2581   {
2582 #if defined(TARGET_SDL2)
2583     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2584 #else
2585     HDROP hdrop = (HDROP)syswmmsg->wParam;
2586 #endif
2587     int i, num_files;
2588
2589     printf("::: SDL_SYSWMEVENT:\n");
2590
2591     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2592
2593     for (i = 0; i < num_files; i++)
2594     {
2595       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2596       char buffer[buffer_len + 1];
2597
2598       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2599
2600       printf("::: - '%s'\n", buffer);
2601     }
2602
2603 #if defined(TARGET_SDL2)
2604     DragFinish((HDROP)syswmmsg->msg.win.wParam);
2605 #else
2606     DragFinish((HDROP)syswmmsg->wParam);
2607 #endif
2608   }
2609 #endif
2610 #endif
2611 }
2612
2613
2614 /* ========================================================================= */
2615 /* joystick functions                                                        */
2616 /* ========================================================================= */
2617
2618 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2619 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2620 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2621
2622 static boolean SDLOpenJoystick(int nr)
2623 {
2624   if (nr < 0 || nr > MAX_PLAYERS)
2625     return FALSE;
2626
2627   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2628 }
2629
2630 static void SDLCloseJoystick(int nr)
2631 {
2632   if (nr < 0 || nr > MAX_PLAYERS)
2633     return;
2634
2635   SDL_JoystickClose(sdl_joystick[nr]);
2636
2637   sdl_joystick[nr] = NULL;
2638 }
2639
2640 static boolean SDLCheckJoystickOpened(int nr)
2641 {
2642   if (nr < 0 || nr > MAX_PLAYERS)
2643     return FALSE;
2644
2645 #if defined(TARGET_SDL2)
2646   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2647 #else
2648   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2649 #endif
2650 }
2651
2652 void HandleJoystickEvent(Event *event)
2653 {
2654   switch(event->type)
2655   {
2656     case SDL_JOYAXISMOTION:
2657       if (event->jaxis.axis < 2)
2658         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2659       break;
2660
2661     case SDL_JOYBUTTONDOWN:
2662       if (event->jbutton.button < 2)
2663         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2664       break;
2665
2666     case SDL_JOYBUTTONUP:
2667       if (event->jbutton.button < 2)
2668         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2669       break;
2670
2671     default:
2672       break;
2673   }
2674 }
2675
2676 void SDLInitJoysticks()
2677 {
2678   static boolean sdl_joystick_subsystem_initialized = FALSE;
2679   boolean print_warning = !sdl_joystick_subsystem_initialized;
2680   int i;
2681
2682   if (!sdl_joystick_subsystem_initialized)
2683   {
2684     sdl_joystick_subsystem_initialized = TRUE;
2685
2686     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2687     {
2688       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2689       return;
2690     }
2691   }
2692
2693   for (i = 0; i < MAX_PLAYERS; i++)
2694   {
2695     /* get configured joystick for this player */
2696     char *device_name = setup.input[i].joy.device_name;
2697     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2698
2699     if (joystick_nr >= SDL_NumJoysticks())
2700     {
2701       if (setup.input[i].use_joystick && print_warning)
2702         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2703
2704       joystick_nr = -1;
2705     }
2706
2707     /* misuse joystick file descriptor variable to store joystick number */
2708     joystick.fd[i] = joystick_nr;
2709
2710     if (joystick_nr == -1)
2711       continue;
2712
2713     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2714     if (SDLCheckJoystickOpened(joystick_nr))
2715       SDLCloseJoystick(joystick_nr);
2716
2717     if (!setup.input[i].use_joystick)
2718       continue;
2719
2720     if (!SDLOpenJoystick(joystick_nr))
2721     {
2722       if (print_warning)
2723         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2724
2725       continue;
2726     }
2727
2728     joystick.status = JOYSTICK_ACTIVATED;
2729   }
2730 }
2731
2732 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2733 {
2734   if (nr < 0 || nr >= MAX_PLAYERS)
2735     return FALSE;
2736
2737   if (x != NULL)
2738     *x = sdl_js_axis[nr][0];
2739   if (y != NULL)
2740     *y = sdl_js_axis[nr][1];
2741
2742   if (b1 != NULL)
2743     *b1 = sdl_js_button[nr][0];
2744   if (b2 != NULL)
2745     *b2 = sdl_js_button[nr][1];
2746
2747   return TRUE;
2748 }