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