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