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