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