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