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