changed screen rendering mode from preprocessor to runtime configurable
[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 SDLSetWindowFullscreen(boolean fullscreen)
746 {
747   if (sdl_window == NULL)
748     return;
749
750   int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
751
752   if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
753     video.fullscreen_enabled = fullscreen_enabled = fullscreen;
754
755   // if screen size was changed in fullscreen mode, correct desktop window size
756   if (!fullscreen && video.fullscreen_initial)
757   {
758     SDLSetWindowScaling(setup.window_scaling_percent);
759     SDL_SetWindowPosition(sdl_window,
760                           SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
761
762     video.fullscreen_initial = FALSE;
763   }
764 }
765
766 void SDLRedrawWindow()
767 {
768   UpdateScreen(NULL);
769 }
770 #endif
771
772 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
773                             int depth)
774 {
775   SDL_Surface *surface =
776     SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
777
778   if (surface == NULL)
779     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
780
781   SDLSetNativeSurface(&surface);
782
783   bitmap->surface = surface;
784 }
785
786 void SDLFreeBitmapPointers(Bitmap *bitmap)
787 {
788   if (bitmap->surface)
789     SDL_FreeSurface(bitmap->surface);
790   if (bitmap->surface_masked)
791     SDL_FreeSurface(bitmap->surface_masked);
792
793   bitmap->surface = NULL;
794   bitmap->surface_masked = NULL;
795
796 #if defined(TARGET_SDL2)
797   if (bitmap->texture)
798     SDL_DestroyTexture(bitmap->texture);
799   if (bitmap->texture_masked)
800     SDL_DestroyTexture(bitmap->texture_masked);
801
802   bitmap->texture = NULL;
803   bitmap->texture_masked = NULL;
804 #endif
805 }
806
807 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
808                  int src_x, int src_y, int width, int height,
809                  int dst_x, int dst_y, int mask_mode)
810 {
811   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
812   SDL_Rect src_rect, dst_rect;
813
814   src_rect.x = src_x;
815   src_rect.y = src_y;
816   src_rect.w = width;
817   src_rect.h = height;
818
819   dst_rect.x = dst_x;
820   dst_rect.y = dst_y;
821   dst_rect.w = width;
822   dst_rect.h = height;
823
824   // if (src_bitmap != backbuffer || dst_bitmap != window)
825   if (!(src_bitmap == backbuffer && dst_bitmap == window))
826     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
827                      src_bitmap->surface_masked : src_bitmap->surface),
828                     &src_rect, real_dst_bitmap->surface, &dst_rect);
829
830   if (dst_bitmap == window)
831     UpdateScreen(&dst_rect);
832 }
833
834 void SDLBlitTexture(Bitmap *bitmap,
835                     int src_x, int src_y, int width, int height,
836                     int dst_x, int dst_y, int mask_mode)
837 {
838 #if defined(TARGET_SDL2)
839   SDL_Texture *texture;
840   SDL_Rect src_rect;
841   SDL_Rect dst_rect;
842
843   texture =
844     (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
845
846   if (texture == NULL)
847     return;
848
849   src_rect.x = src_x;
850   src_rect.y = src_y;
851   src_rect.w = width;
852   src_rect.h = height;
853
854   dst_rect.x = dst_x;
855   dst_rect.y = dst_y;
856   dst_rect.w = width;
857   dst_rect.h = height;
858
859   SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
860 #endif
861 }
862
863 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
864                       Uint32 color)
865 {
866   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
867   SDL_Rect rect;
868
869   rect.x = x;
870   rect.y = y;
871   rect.w = width;
872   rect.h = height;
873
874   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
875
876   if (dst_bitmap == window)
877     UpdateScreen(&rect);
878 }
879
880 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
881                       int fade_mode, int fade_delay, int post_delay,
882                       void (*draw_border_function)(void))
883 {
884   SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
885   SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
886   SDL_Surface *surface_black  = gfx.fade_bitmap_black->surface;
887   SDL_Surface *surface_screen = backbuffer->surface;
888   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
889   SDL_Rect src_rect, dst_rect;
890   SDL_Rect dst_rect2;
891   int src_x = x, src_y = y;
892   int dst_x = x, dst_y = y;
893   unsigned int time_last, time_current;
894
895   // store function for drawing global masked border
896   void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
897
898   // deactivate drawing of global border while fading, if needed
899   if (draw_border_function == NULL)
900     gfx.draw_global_border_function = NULL;
901
902   src_rect.x = src_x;
903   src_rect.y = src_y;
904   src_rect.w = width;
905   src_rect.h = height;
906
907   dst_rect.x = dst_x;
908   dst_rect.y = dst_y;
909   dst_rect.w = width;           /* (ignored) */
910   dst_rect.h = height;          /* (ignored) */
911
912   dst_rect2 = dst_rect;
913
914   /* copy source and target surfaces to temporary surfaces for fading */
915   if (fade_mode & FADE_TYPE_TRANSFORM)
916   {
917     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
918     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
919
920     draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
921     draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
922   }
923   else if (fade_mode & FADE_TYPE_FADE_IN)
924   {
925     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
926     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
927
928     draw_global_border_function(DRAW_BORDER_TO_FADE_TARGET);
929   }
930   else          /* FADE_TYPE_FADE_OUT */
931   {
932     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
933     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
934
935     draw_global_border_function(DRAW_BORDER_TO_FADE_SOURCE);
936   }
937
938   time_current = SDL_GetTicks();
939
940   if (fade_mode == FADE_MODE_MELT)
941   {
942     boolean done = FALSE;
943     int melt_pixels = 2;
944     int melt_columns = width / melt_pixels;
945     int ypos[melt_columns];
946     int max_steps = height / 8 + 32;
947     int steps_done = 0;
948     float steps = 0;
949     int i;
950
951     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
952 #if defined(TARGET_SDL2)
953     SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_NONE);
954 #else
955     SDL_SetAlpha(surface_target, 0, 0);         /* disable alpha blending */
956 #endif
957
958     ypos[0] = -GetSimpleRandom(16);
959
960     for (i = 1 ; i < melt_columns; i++)
961     {
962       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
963
964       ypos[i] = ypos[i - 1] + r;
965
966       if (ypos[i] > 0)
967         ypos[i] = 0;
968       else
969         if (ypos[i] == -16)
970           ypos[i] = -15;
971     }
972
973     while (!done)
974     {
975       int steps_final;
976
977       time_last = time_current;
978       time_current = SDL_GetTicks();
979       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
980       steps_final = MIN(MAX(0, steps), max_steps);
981
982       steps_done++;
983
984       done = (steps_done >= steps_final);
985
986       for (i = 0 ; i < melt_columns; i++)
987       {
988         if (ypos[i] < 0)
989         {
990           ypos[i]++;
991
992           done = FALSE;
993         }
994         else if (ypos[i] < height)
995         {
996           int y1 = 16;
997           int y2 = 8;
998           int y3 = 8;
999           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1000
1001           if (ypos[i] + dy >= height)
1002             dy = height - ypos[i];
1003
1004           /* copy part of (appearing) target surface to upper area */
1005           src_rect.x = src_x + i * melt_pixels;
1006           // src_rect.y = src_y + ypos[i];
1007           src_rect.y = src_y;
1008           src_rect.w = melt_pixels;
1009           // src_rect.h = dy;
1010           src_rect.h = ypos[i] + dy;
1011
1012           dst_rect.x = dst_x + i * melt_pixels;
1013           // dst_rect.y = dst_y + ypos[i];
1014           dst_rect.y = dst_y;
1015
1016           if (steps_done >= steps_final)
1017             SDL_BlitSurface(surface_target, &src_rect,
1018                             surface_screen, &dst_rect);
1019
1020           ypos[i] += dy;
1021
1022           /* copy part of (disappearing) source surface to lower area */
1023           src_rect.x = src_x + i * melt_pixels;
1024           src_rect.y = src_y;
1025           src_rect.w = melt_pixels;
1026           src_rect.h = height - ypos[i];
1027
1028           dst_rect.x = dst_x + i * melt_pixels;
1029           dst_rect.y = dst_y + ypos[i];
1030
1031           if (steps_done >= steps_final)
1032             SDL_BlitSurface(surface_source, &src_rect,
1033                             surface_screen, &dst_rect);
1034
1035           done = FALSE;
1036         }
1037         else
1038         {
1039           src_rect.x = src_x + i * melt_pixels;
1040           src_rect.y = src_y;
1041           src_rect.w = melt_pixels;
1042           src_rect.h = height;
1043
1044           dst_rect.x = dst_x + i * melt_pixels;
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       }
1052
1053       if (steps_done >= steps_final)
1054       {
1055         if (draw_border_function != NULL)
1056           draw_border_function();
1057
1058         UpdateScreen(&dst_rect2);
1059       }
1060     }
1061   }
1062   else if (fade_mode == FADE_MODE_CURTAIN)
1063   {
1064     float xx;
1065     int xx_final;
1066     int xx_size = width / 2;
1067
1068     SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1069 #if defined(TARGET_SDL2)
1070     SDL_SetSurfaceBlendMode(surface_source, SDL_BLENDMODE_NONE);
1071 #else
1072     SDL_SetAlpha(surface_source, 0, 0);         /* disable alpha blending */
1073 #endif
1074
1075     for (xx = 0; xx < xx_size;)
1076     {
1077       time_last = time_current;
1078       time_current = SDL_GetTicks();
1079       xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1080       xx_final = MIN(MAX(0, xx), xx_size);
1081
1082       src_rect.x = src_x;
1083       src_rect.y = src_y;
1084       src_rect.w = width;
1085       src_rect.h = height;
1086
1087       dst_rect.x = dst_x;
1088       dst_rect.y = dst_y;
1089
1090       /* draw new (target) image to screen buffer */
1091       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1092
1093       if (xx_final < xx_size)
1094       {
1095         src_rect.w = xx_size - xx_final;
1096         src_rect.h = height;
1097
1098         /* draw old (source) image to screen buffer (left side) */
1099
1100         src_rect.x = src_x + xx_final;
1101         dst_rect.x = dst_x;
1102
1103         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1104
1105         /* draw old (source) image to screen buffer (right side) */
1106
1107         src_rect.x = src_x + xx_size;
1108         dst_rect.x = dst_x + xx_size + xx_final;
1109
1110         SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1111       }
1112
1113       if (draw_border_function != NULL)
1114         draw_border_function();
1115
1116       /* only update the region of the screen that is affected from fading */
1117       UpdateScreen(&dst_rect2);
1118     }
1119   }
1120   else          /* fading in, fading out or cross-fading */
1121   {
1122     float alpha;
1123     int alpha_final;
1124
1125     for (alpha = 0.0; alpha < 255.0;)
1126     {
1127       time_last = time_current;
1128       time_current = SDL_GetTicks();
1129       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1130       alpha_final = MIN(MAX(0, alpha), 255);
1131
1132       /* draw existing (source) image to screen buffer */
1133       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1134
1135       /* draw new (target) image to screen buffer using alpha blending */
1136 #if defined(TARGET_SDL2)
1137       SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
1138       SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
1139 #else
1140       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
1141 #endif
1142       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
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(&dst_rect);
1149     }
1150   }
1151
1152   if (post_delay > 0)
1153   {
1154     unsigned int time_post_delay;
1155
1156     time_current = SDL_GetTicks();
1157     time_post_delay = time_current + post_delay;
1158
1159     while (time_current < time_post_delay)
1160     {
1161       // do not wait longer than 10 ms at a time to be able to ...
1162       Delay(MIN(10, time_post_delay - time_current));
1163
1164       // ... continue drawing global animations during post delay
1165       UpdateScreen(NULL);
1166
1167       time_current = SDL_GetTicks();
1168     }
1169   }
1170
1171   // restore function for drawing global masked border
1172   gfx.draw_global_border_function = draw_global_border_function;
1173 }
1174
1175 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1176                        int to_x, int to_y, Uint32 color)
1177 {
1178   SDL_Surface *surface = dst_bitmap->surface;
1179   SDL_Rect rect;
1180
1181   if (from_x > to_x)
1182     swap_numbers(&from_x, &to_x);
1183
1184   if (from_y > to_y)
1185     swap_numbers(&from_y, &to_y);
1186
1187   rect.x = from_x;
1188   rect.y = from_y;
1189   rect.w = (to_x - from_x + 1);
1190   rect.h = (to_y - from_y + 1);
1191
1192   SDL_FillRect(surface, &rect, color);
1193 }
1194
1195 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1196                  int to_x, int to_y, Uint32 color)
1197 {
1198   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1199 }
1200
1201 #if ENABLE_UNUSED_CODE
1202 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1203                   int num_points, Uint32 color)
1204 {
1205   int i, x, y;
1206   int line_width = 4;
1207
1208   for (i = 0; i < num_points - 1; i++)
1209   {
1210     for (x = 0; x < line_width; x++)
1211     {
1212       for (y = 0; y < line_width; y++)
1213       {
1214         int dx = x - line_width / 2;
1215         int dy = y - line_width / 2;
1216
1217         if ((x == 0 && y == 0) ||
1218             (x == 0 && y == line_width - 1) ||
1219             (x == line_width - 1 && y == 0) ||
1220             (x == line_width - 1 && y == line_width - 1))
1221           continue;
1222
1223         sge_Line(surface, points[i].x + dx, points[i].y + dy,
1224                  points[i+1].x + dx, points[i+1].y + dy, color);
1225       }
1226     }
1227   }
1228 }
1229 #endif
1230
1231 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1232 {
1233   SDL_Surface *surface = src_bitmap->surface;
1234
1235   switch (surface->format->BytesPerPixel)
1236   {
1237     case 1:             /* assuming 8-bpp */
1238     {
1239       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1240     }
1241     break;
1242
1243     case 2:             /* probably 15-bpp or 16-bpp */
1244     {
1245       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1246     }
1247     break;
1248
1249   case 3:               /* slow 24-bpp mode; usually not used */
1250     {
1251       /* does this work? */
1252       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1253       Uint32 color = 0;
1254       int shift;
1255
1256       shift = surface->format->Rshift;
1257       color |= *(pix + shift / 8) >> shift;
1258       shift = surface->format->Gshift;
1259       color |= *(pix + shift / 8) >> shift;
1260       shift = surface->format->Bshift;
1261       color |= *(pix + shift / 8) >> shift;
1262
1263       return color;
1264     }
1265     break;
1266
1267   case 4:               /* probably 32-bpp */
1268     {
1269       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1270     }
1271     break;
1272   }
1273
1274   return 0;
1275 }
1276
1277
1278 /* ========================================================================= */
1279 /* The following functions were taken from the SGE library                   */
1280 /* (SDL Graphics Extension Library) by Anders Lindström                      */
1281 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
1282 /* ========================================================================= */
1283
1284 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1285 {
1286   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1287   {
1288     switch (surface->format->BytesPerPixel)
1289     {
1290       case 1:
1291       {
1292         /* Assuming 8-bpp */
1293         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1294       }
1295       break;
1296
1297       case 2:
1298       {
1299         /* Probably 15-bpp or 16-bpp */
1300         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1301       }
1302       break;
1303
1304       case 3:
1305       {
1306         /* Slow 24-bpp mode, usually not used */
1307         Uint8 *pix;
1308         int shift;
1309
1310         /* Gack - slow, but endian correct */
1311         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1312         shift = surface->format->Rshift;
1313         *(pix+shift/8) = color>>shift;
1314         shift = surface->format->Gshift;
1315         *(pix+shift/8) = color>>shift;
1316         shift = surface->format->Bshift;
1317         *(pix+shift/8) = color>>shift;
1318       }
1319       break;
1320
1321       case 4:
1322       {
1323         /* Probably 32-bpp */
1324         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1325       }
1326       break;
1327     }
1328   }
1329 }
1330
1331 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1332                   Uint8 R, Uint8 G, Uint8 B)
1333 {
1334   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1335 }
1336
1337 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1338 {
1339   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1340 }
1341
1342 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1343 {
1344   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1345 }
1346
1347 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1348 {
1349   Uint8 *pix;
1350   int shift;
1351
1352   /* Gack - slow, but endian correct */
1353   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1354   shift = surface->format->Rshift;
1355   *(pix+shift/8) = color>>shift;
1356   shift = surface->format->Gshift;
1357   *(pix+shift/8) = color>>shift;
1358   shift = surface->format->Bshift;
1359   *(pix+shift/8) = color>>shift;
1360 }
1361
1362 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1363 {
1364   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1365 }
1366
1367 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1368 {
1369   switch (dest->format->BytesPerPixel)
1370   {
1371     case 1:
1372       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1373       break;
1374
1375     case 2:
1376       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1377       break;
1378
1379     case 3:
1380       _PutPixel24(dest,x,y,color);
1381       break;
1382
1383     case 4:
1384       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1385       break;
1386   }
1387 }
1388
1389 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1390 {
1391   if (SDL_MUSTLOCK(surface))
1392   {
1393     if (SDL_LockSurface(surface) < 0)
1394     {
1395       return;
1396     }
1397   }
1398
1399   _PutPixel(surface, x, y, color);
1400
1401   if (SDL_MUSTLOCK(surface))
1402   {
1403     SDL_UnlockSurface(surface);
1404   }
1405 }
1406
1407 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1408                   Uint8 r, Uint8 g, Uint8 b)
1409 {
1410   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1411 }
1412
1413 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1414 {
1415   if (y >= 0 && y <= dest->h - 1)
1416   {
1417     switch (dest->format->BytesPerPixel)
1418     {
1419       case 1:
1420         return y*dest->pitch;
1421         break;
1422
1423       case 2:
1424         return y*dest->pitch/2;
1425         break;
1426
1427       case 3:
1428         return y*dest->pitch;
1429         break;
1430
1431       case 4:
1432         return y*dest->pitch/4;
1433         break;
1434     }
1435   }
1436
1437   return -1;
1438 }
1439
1440 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1441 {
1442   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1443   {
1444     switch (surface->format->BytesPerPixel)
1445     {
1446       case 1:
1447       {
1448         /* Assuming 8-bpp */
1449         *((Uint8 *)surface->pixels + ypitch + x) = color;
1450       }
1451       break;
1452
1453       case 2:
1454       {
1455         /* Probably 15-bpp or 16-bpp */
1456         *((Uint16 *)surface->pixels + ypitch + x) = color;
1457       }
1458       break;
1459
1460       case 3:
1461       {
1462         /* Slow 24-bpp mode, usually not used */
1463         Uint8 *pix;
1464         int shift;
1465
1466         /* Gack - slow, but endian correct */
1467         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1468         shift = surface->format->Rshift;
1469         *(pix+shift/8) = color>>shift;
1470         shift = surface->format->Gshift;
1471         *(pix+shift/8) = color>>shift;
1472         shift = surface->format->Bshift;
1473         *(pix+shift/8) = color>>shift;
1474       }
1475       break;
1476
1477       case 4:
1478       {
1479         /* Probably 32-bpp */
1480         *((Uint32 *)surface->pixels + ypitch + x) = color;
1481       }
1482       break;
1483     }
1484   }
1485 }
1486
1487 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1488                Uint32 Color)
1489 {
1490   SDL_Rect l;
1491
1492   if (SDL_MUSTLOCK(Surface))
1493   {
1494     if (SDL_LockSurface(Surface) < 0)
1495     {
1496       return;
1497     }
1498   }
1499
1500   if (x1 > x2)
1501   {
1502     Sint16 tmp = x1;
1503     x1 = x2;
1504     x2 = tmp;
1505   }
1506
1507   /* Do the clipping */
1508   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1509     return;
1510   if (x1 < 0)
1511     x1 = 0;
1512   if (x2 > Surface->w - 1)
1513     x2 = Surface->w - 1;
1514
1515   l.x = x1;
1516   l.y = y;
1517   l.w = x2 - x1 + 1;
1518   l.h = 1;
1519
1520   SDL_FillRect(Surface, &l, Color);
1521
1522   if (SDL_MUSTLOCK(Surface))
1523   {
1524     SDL_UnlockSurface(Surface);
1525   }
1526 }
1527
1528 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1529                   Uint8 R, Uint8 G, Uint8 B)
1530 {
1531   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1532 }
1533
1534 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1535 {
1536   SDL_Rect l;
1537
1538   if (x1 > x2)
1539   {
1540     Sint16 tmp = x1;
1541     x1 = x2;
1542     x2 = tmp;
1543   }
1544
1545   /* Do the clipping */
1546   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1547     return;
1548   if (x1 < 0)
1549     x1 = 0;
1550   if (x2 > Surface->w - 1)
1551     x2 = Surface->w - 1;
1552
1553   l.x = x1;
1554   l.y = y;
1555   l.w = x2 - x1 + 1;
1556   l.h = 1;
1557
1558   SDL_FillRect(Surface, &l, Color);
1559 }
1560
1561 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1562                Uint32 Color)
1563 {
1564   SDL_Rect l;
1565
1566   if (SDL_MUSTLOCK(Surface))
1567   {
1568     if (SDL_LockSurface(Surface) < 0)
1569     {
1570       return;
1571     }
1572   }
1573
1574   if (y1 > y2)
1575   {
1576     Sint16 tmp = y1;
1577     y1 = y2;
1578     y2 = tmp;
1579   }
1580
1581   /* Do the clipping */
1582   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1583     return;
1584   if (y1 < 0)
1585     y1 = 0;
1586   if (y2 > Surface->h - 1)
1587     y2 = Surface->h - 1;
1588
1589   l.x = x;
1590   l.y = y1;
1591   l.w = 1;
1592   l.h = y2 - y1 + 1;
1593
1594   SDL_FillRect(Surface, &l, Color);
1595
1596   if (SDL_MUSTLOCK(Surface))
1597   {
1598     SDL_UnlockSurface(Surface);
1599   }
1600 }
1601
1602 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1603                   Uint8 R, Uint8 G, Uint8 B)
1604 {
1605   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1606 }
1607
1608 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1609 {
1610   SDL_Rect l;
1611
1612   if (y1 > y2)
1613   {
1614     Sint16 tmp = y1;
1615     y1 = y2;
1616     y2 = tmp;
1617   }
1618
1619   /* Do the clipping */
1620   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1621     return;
1622   if (y1 < 0)
1623     y1 = 0;
1624   if (y2 > Surface->h - 1)
1625     y2 = Surface->h - 1;
1626
1627   l.x = x;
1628   l.y = y1;
1629   l.w = 1;
1630   l.h = y2 - y1 + 1;
1631
1632   SDL_FillRect(Surface, &l, Color);
1633 }
1634
1635 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1636                 Sint16 x2, Sint16 y2, Uint32 Color,
1637                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1638                               Uint32 Color))
1639 {
1640   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1641
1642   dx = x2 - x1;
1643   dy = y2 - y1;
1644
1645   sdx = (dx < 0) ? -1 : 1;
1646   sdy = (dy < 0) ? -1 : 1;
1647
1648   dx = sdx * dx + 1;
1649   dy = sdy * dy + 1;
1650
1651   x = y = 0;
1652
1653   px = x1;
1654   py = y1;
1655
1656   if (dx >= dy)
1657   {
1658     for (x = 0; x < dx; x++)
1659     {
1660       Callback(Surface, px, py, Color);
1661
1662       y += dy;
1663       if (y >= dx)
1664       {
1665         y -= dx;
1666         py += sdy;
1667       }
1668
1669       px += sdx;
1670     }
1671   }
1672   else
1673   {
1674     for (y = 0; y < dy; y++)
1675     {
1676       Callback(Surface, px, py, Color);
1677
1678       x += dx;
1679       if (x >= dy)
1680       {
1681         x -= dy;
1682         px += sdx;
1683       }
1684
1685       py += sdy;
1686     }
1687   }
1688 }
1689
1690 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1691                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1692                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1693                                  Uint32 Color))
1694 {
1695   sge_DoLine(Surface, X1, Y1, X2, Y2,
1696              SDL_MapRGB(Surface->format, R, G, B), Callback);
1697 }
1698
1699 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1700               Uint32 Color)
1701 {
1702   if (SDL_MUSTLOCK(Surface))
1703   {
1704     if (SDL_LockSurface(Surface) < 0)
1705       return;
1706    }
1707
1708    /* Draw the line */
1709    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1710
1711    /* unlock the display */
1712    if (SDL_MUSTLOCK(Surface))
1713    {
1714       SDL_UnlockSurface(Surface);
1715    }
1716 }
1717
1718 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1719                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1720 {
1721   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1722 }
1723
1724 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1725 {
1726   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1727 }
1728
1729
1730 /*
1731   -----------------------------------------------------------------------------
1732   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1733   -----------------------------------------------------------------------------
1734 */
1735
1736 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1737                    int width, int height, Uint32 color)
1738 {
1739   int x, y;
1740
1741   for (y = src_y; y < src_y + height; y++)
1742   {
1743     for (x = src_x; x < src_x + width; x++)
1744     {
1745       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1746
1747       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1748     }
1749   }
1750 }
1751
1752 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1753                           int src_x, int src_y, int width, int height,
1754                           int dst_x, int dst_y)
1755 {
1756   int x, y;
1757
1758   for (y = 0; y < height; y++)
1759   {
1760     for (x = 0; x < width; x++)
1761     {
1762       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1763
1764       if (pixel != BLACK_PIXEL)
1765         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1766     }
1767   }
1768 }
1769
1770
1771 /* ========================================================================= */
1772 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1773 /* (Rotozoomer) by Andreas Schiffler                                         */
1774 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1775 /* ========================================================================= */
1776
1777 /*
1778   -----------------------------------------------------------------------------
1779   32 bit zoomer
1780
1781   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1782   -----------------------------------------------------------------------------
1783 */
1784
1785 typedef struct
1786 {
1787   Uint8 r;
1788   Uint8 g;
1789   Uint8 b;
1790   Uint8 a;
1791 } tColorRGBA;
1792
1793 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1794 {
1795   int x, y;
1796   tColorRGBA *sp, *csp, *dp;
1797   int dgap;
1798
1799   /* pointer setup */
1800   sp = csp = (tColorRGBA *) src->pixels;
1801   dp = (tColorRGBA *) dst->pixels;
1802   dgap = dst->pitch - dst->w * 4;
1803
1804   for (y = 0; y < dst->h; y++)
1805   {
1806     sp = csp;
1807
1808     for (x = 0; x < dst->w; x++)
1809     {
1810       tColorRGBA *sp0 = sp;
1811       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1812       tColorRGBA *sp00 = &sp0[0];
1813       tColorRGBA *sp01 = &sp0[1];
1814       tColorRGBA *sp10 = &sp1[0];
1815       tColorRGBA *sp11 = &sp1[1];
1816       tColorRGBA new;
1817
1818       /* create new color pixel from all four source color pixels */
1819       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1820       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1821       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1822       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1823
1824       /* draw */
1825       *dp = new;
1826
1827       /* advance source pointers */
1828       sp += 2;
1829
1830       /* advance destination pointer */
1831       dp++;
1832     }
1833
1834     /* advance source pointer */
1835     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1836
1837     /* advance destination pointers */
1838     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1839   }
1840
1841   return 0;
1842 }
1843
1844 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1845 {
1846   int x, y, *sax, *say, *csax, *csay;
1847   float sx, sy;
1848   tColorRGBA *sp, *csp, *csp0, *dp;
1849   int dgap;
1850
1851   /* use specialized zoom function when scaling down to exactly half size */
1852   if (src->w == 2 * dst->w &&
1853       src->h == 2 * dst->h)
1854     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1855
1856   /* variable setup */
1857   sx = (float) src->w / (float) dst->w;
1858   sy = (float) src->h / (float) dst->h;
1859
1860   /* allocate memory for row increments */
1861   csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1862   csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1863
1864   /* precalculate row increments */
1865   for (x = 0; x <= dst->w; x++)
1866     *csax++ = (int)(sx * x);
1867
1868   for (y = 0; y <= dst->h; y++)
1869     *csay++ = (int)(sy * y);
1870
1871   /* pointer setup */
1872   sp = csp = csp0 = (tColorRGBA *) src->pixels;
1873   dp = (tColorRGBA *) dst->pixels;
1874   dgap = dst->pitch - dst->w * 4;
1875
1876   csay = say;
1877   for (y = 0; y < dst->h; y++)
1878   {
1879     sp = csp;
1880     csax = sax;
1881
1882     for (x = 0; x < dst->w; x++)
1883     {
1884       /* draw */
1885       *dp = *sp;
1886
1887       /* advance source pointers */
1888       csax++;
1889       sp = csp + *csax;
1890
1891       /* advance destination pointer */
1892       dp++;
1893     }
1894
1895     /* advance source pointer */
1896     csay++;
1897     csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
1898
1899     /* advance destination pointers */
1900     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1901   }
1902
1903   free(sax);
1904   free(say);
1905
1906   return 0;
1907 }
1908
1909 /*
1910   -----------------------------------------------------------------------------
1911   8 bit zoomer
1912
1913   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1914   -----------------------------------------------------------------------------
1915 */
1916
1917 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1918 {
1919   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1920   Uint8 *sp, *dp, *csp;
1921   int dgap;
1922
1923   /* variable setup */
1924   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1925   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1926
1927   /* allocate memory for row increments */
1928   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1929   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1930
1931   /* precalculate row increments */
1932   csx = 0;
1933   csax = sax;
1934   for (x = 0; x < dst->w; x++)
1935   {
1936     csx += sx;
1937     *csax = (csx >> 16);
1938     csx &= 0xffff;
1939     csax++;
1940   }
1941
1942   csy = 0;
1943   csay = say;
1944   for (y = 0; y < dst->h; y++)
1945   {
1946     csy += sy;
1947     *csay = (csy >> 16);
1948     csy &= 0xffff;
1949     csay++;
1950   }
1951
1952   csx = 0;
1953   csax = sax;
1954   for (x = 0; x < dst->w; x++)
1955   {
1956     csx += (*csax);
1957     csax++;
1958   }
1959
1960   csy = 0;
1961   csay = say;
1962   for (y = 0; y < dst->h; y++)
1963   {
1964     csy += (*csay);
1965     csay++;
1966   }
1967
1968   /* pointer setup */
1969   sp = csp = (Uint8 *) src->pixels;
1970   dp = (Uint8 *) dst->pixels;
1971   dgap = dst->pitch - dst->w;
1972
1973   /* draw */
1974   csay = say;
1975   for (y = 0; y < dst->h; y++)
1976   {
1977     csax = sax;
1978     sp = csp;
1979     for (x = 0; x < dst->w; x++)
1980     {
1981       /* draw */
1982       *dp = *sp;
1983
1984       /* advance source pointers */
1985       sp += (*csax);
1986       csax++;
1987
1988       /* advance destination pointer */
1989       dp++;
1990     }
1991
1992     /* advance source pointer (for row) */
1993     csp += ((*csay) * src->pitch);
1994     csay++;
1995
1996     /* advance destination pointers */
1997     dp += dgap;
1998   }
1999
2000   free(sax);
2001   free(say);
2002
2003   return 0;
2004 }
2005
2006 /*
2007   -----------------------------------------------------------------------------
2008   zoomSurface()
2009
2010   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2011   'zoomx' and 'zoomy' are scaling factors for width and height.
2012   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2013   into a 32bit RGBA format on the fly.
2014   -----------------------------------------------------------------------------
2015 */
2016
2017 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2018 {
2019   SDL_Surface *zoom_src = NULL;
2020   SDL_Surface *zoom_dst = NULL;
2021   boolean is_converted = FALSE;
2022   boolean is_32bit;
2023   int i;
2024
2025   if (src == NULL)
2026     return NULL;
2027
2028   /* determine if source surface is 32 bit or 8 bit */
2029   is_32bit = (src->format->BitsPerPixel == 32);
2030
2031   if (is_32bit || src->format->BitsPerPixel == 8)
2032   {
2033     /* use source surface 'as is' */
2034     zoom_src = src;
2035   }
2036   else
2037   {
2038     /* new source surface is 32 bit with a defined RGB ordering */
2039     zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2040                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
2041     SDL_BlitSurface(src, NULL, zoom_src, NULL);
2042     is_32bit = TRUE;
2043     is_converted = TRUE;
2044   }
2045
2046   /* allocate surface to completely contain the zoomed surface */
2047   if (is_32bit)
2048   {
2049     /* target surface is 32 bit with source RGBA/ABGR ordering */
2050     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2051                                     zoom_src->format->Rmask,
2052                                     zoom_src->format->Gmask,
2053                                     zoom_src->format->Bmask, 0);
2054   }
2055   else
2056   {
2057     /* target surface is 8 bit */
2058     zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2059                                     0, 0, 0, 0);
2060   }
2061
2062   /* lock source surface */
2063   SDL_LockSurface(zoom_src);
2064
2065   /* check which kind of surface we have */
2066   if (is_32bit)
2067   {
2068     /* call the 32 bit transformation routine to do the zooming */
2069     zoomSurfaceRGBA(zoom_src, zoom_dst);
2070   }
2071   else
2072   {
2073     /* copy palette */
2074     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2075       zoom_dst->format->palette->colors[i] =
2076         zoom_src->format->palette->colors[i];
2077     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2078
2079     /* call the 8 bit transformation routine to do the zooming */
2080     zoomSurfaceY(zoom_src, zoom_dst);
2081   }
2082
2083   /* unlock source surface */
2084   SDL_UnlockSurface(zoom_src);
2085
2086   /* free temporary surface */
2087   if (is_converted)
2088     SDL_FreeSurface(zoom_src);
2089
2090   /* return destination surface */
2091   return zoom_dst;
2092 }
2093
2094 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2095 {
2096   Bitmap *dst_bitmap = CreateBitmapStruct();
2097   SDL_Surface **dst_surface = &dst_bitmap->surface;
2098
2099   dst_width  = MAX(1, dst_width);       /* prevent zero bitmap width */
2100   dst_height = MAX(1, dst_height);      /* prevent zero bitmap height */
2101
2102   dst_bitmap->width  = dst_width;
2103   dst_bitmap->height = dst_height;
2104
2105   /* create zoomed temporary surface from source surface */
2106   *dst_surface = zoomSurface(src_bitmap->surface, dst_width, dst_height);
2107
2108   /* create native format destination surface from zoomed temporary surface */
2109   SDLSetNativeSurface(dst_surface);
2110
2111   return dst_bitmap;
2112 }
2113
2114
2115 /* ========================================================================= */
2116 /* load image to bitmap                                                      */
2117 /* ========================================================================= */
2118
2119 Bitmap *SDLLoadImage(char *filename)
2120 {
2121   Bitmap *new_bitmap = CreateBitmapStruct();
2122   SDL_Surface *sdl_image_tmp;
2123
2124   print_timestamp_init("SDLLoadImage");
2125
2126   print_timestamp_time(getBaseNamePtr(filename));
2127
2128   /* load image to temporary surface */
2129   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2130   {
2131     SetError("IMG_Load(): %s", SDL_GetError());
2132
2133     return NULL;
2134   }
2135
2136   print_timestamp_time("IMG_Load");
2137
2138   UPDATE_BUSY_STATE();
2139
2140   /* create native non-transparent surface for current image */
2141   if ((new_bitmap->surface = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2142   {
2143     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2144
2145     return NULL;
2146   }
2147
2148   print_timestamp_time("SDL_DisplayFormat (opaque)");
2149
2150   UPDATE_BUSY_STATE();
2151
2152   /* create native transparent surface for current image */
2153   if (sdl_image_tmp->format->Amask == 0)
2154     SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2155                     SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2156
2157   if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2158   {
2159     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
2160
2161     return NULL;
2162   }
2163
2164   print_timestamp_time("SDL_DisplayFormat (masked)");
2165
2166   UPDATE_BUSY_STATE();
2167
2168   /* free temporary surface */
2169   SDL_FreeSurface(sdl_image_tmp);
2170
2171   new_bitmap->width = new_bitmap->surface->w;
2172   new_bitmap->height = new_bitmap->surface->h;
2173
2174   print_timestamp_done("SDLLoadImage");
2175
2176   return new_bitmap;
2177 }
2178
2179
2180 /* ------------------------------------------------------------------------- */
2181 /* custom cursor fuctions                                                    */
2182 /* ------------------------------------------------------------------------- */
2183
2184 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2185 {
2186   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2187                           cursor_info->width, cursor_info->height,
2188                           cursor_info->hot_x, cursor_info->hot_y);
2189 }
2190
2191 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2192 {
2193   static struct MouseCursorInfo *last_cursor_info = NULL;
2194   static struct MouseCursorInfo *last_cursor_info2 = NULL;
2195   static SDL_Cursor *cursor_default = NULL;
2196   static SDL_Cursor *cursor_current = NULL;
2197
2198   /* if invoked for the first time, store the SDL default cursor */
2199   if (cursor_default == NULL)
2200     cursor_default = SDL_GetCursor();
2201
2202   /* only create new cursor if cursor info (custom only) has changed */
2203   if (cursor_info != NULL && cursor_info != last_cursor_info)
2204   {
2205     cursor_current = create_cursor(cursor_info);
2206     last_cursor_info = cursor_info;
2207   }
2208
2209   /* only set new cursor if cursor info (custom or NULL) has changed */
2210   if (cursor_info != last_cursor_info2)
2211     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2212
2213   last_cursor_info2 = cursor_info;
2214 }
2215
2216
2217 /* ========================================================================= */
2218 /* audio functions                                                           */
2219 /* ========================================================================= */
2220
2221 void SDLOpenAudio(void)
2222 {
2223 #if !defined(TARGET_SDL2)
2224   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2225     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2226 #endif
2227
2228   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2229   {
2230     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2231     return;
2232   }
2233
2234   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2235                     AUDIO_NUM_CHANNELS_STEREO,
2236                     setup.system.audio_fragment_size) < 0)
2237   {
2238     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2239     return;
2240   }
2241
2242   audio.sound_available = TRUE;
2243   audio.music_available = TRUE;
2244   audio.loops_available = TRUE;
2245   audio.sound_enabled = TRUE;
2246
2247   /* set number of available mixer channels */
2248   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2249   audio.music_channel = MUSIC_CHANNEL;
2250   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2251
2252   Mixer_InitChannels();
2253 }
2254
2255 void SDLCloseAudio(void)
2256 {
2257   Mix_HaltMusic();
2258   Mix_HaltChannel(-1);
2259
2260   Mix_CloseAudio();
2261   SDL_QuitSubSystem(SDL_INIT_AUDIO);
2262 }
2263
2264
2265 /* ========================================================================= */
2266 /* event functions                                                           */
2267 /* ========================================================================= */
2268
2269 void SDLNextEvent(Event *event)
2270 {
2271   SDL_WaitEvent(event);
2272 }
2273
2274 void SDLHandleWindowManagerEvent(Event *event)
2275 {
2276 #ifdef DEBUG
2277 #if defined(PLATFORM_WIN32)
2278   // experimental drag and drop code
2279
2280   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2281   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2282
2283 #if defined(TARGET_SDL2)
2284   if (syswmmsg->msg.win.msg == WM_DROPFILES)
2285 #else
2286   if (syswmmsg->msg == WM_DROPFILES)
2287 #endif
2288   {
2289 #if defined(TARGET_SDL2)
2290     HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2291 #else
2292     HDROP hdrop = (HDROP)syswmmsg->wParam;
2293 #endif
2294     int i, num_files;
2295
2296     printf("::: SDL_SYSWMEVENT:\n");
2297
2298     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2299
2300     for (i = 0; i < num_files; i++)
2301     {
2302       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2303       char buffer[buffer_len + 1];
2304
2305       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2306
2307       printf("::: - '%s'\n", buffer);
2308     }
2309
2310 #if defined(TARGET_SDL2)
2311     DragFinish((HDROP)syswmmsg->msg.win.wParam);
2312 #else
2313     DragFinish((HDROP)syswmmsg->wParam);
2314 #endif
2315   }
2316 #endif
2317 #endif
2318 }
2319
2320
2321 /* ========================================================================= */
2322 /* joystick functions                                                        */
2323 /* ========================================================================= */
2324
2325 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2326 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2327 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2328
2329 static boolean SDLOpenJoystick(int nr)
2330 {
2331   if (nr < 0 || nr > MAX_PLAYERS)
2332     return FALSE;
2333
2334   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2335 }
2336
2337 static void SDLCloseJoystick(int nr)
2338 {
2339   if (nr < 0 || nr > MAX_PLAYERS)
2340     return;
2341
2342   SDL_JoystickClose(sdl_joystick[nr]);
2343
2344   sdl_joystick[nr] = NULL;
2345 }
2346
2347 static boolean SDLCheckJoystickOpened(int nr)
2348 {
2349   if (nr < 0 || nr > MAX_PLAYERS)
2350     return FALSE;
2351
2352 #if defined(TARGET_SDL2)
2353   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2354 #else
2355   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2356 #endif
2357 }
2358
2359 void HandleJoystickEvent(Event *event)
2360 {
2361   switch(event->type)
2362   {
2363     case SDL_JOYAXISMOTION:
2364       if (event->jaxis.axis < 2)
2365         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2366       break;
2367
2368     case SDL_JOYBUTTONDOWN:
2369       if (event->jbutton.button < 2)
2370         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2371       break;
2372
2373     case SDL_JOYBUTTONUP:
2374       if (event->jbutton.button < 2)
2375         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2376       break;
2377
2378     default:
2379       break;
2380   }
2381 }
2382
2383 void SDLInitJoysticks()
2384 {
2385   static boolean sdl_joystick_subsystem_initialized = FALSE;
2386   boolean print_warning = !sdl_joystick_subsystem_initialized;
2387   int i;
2388
2389   if (!sdl_joystick_subsystem_initialized)
2390   {
2391     sdl_joystick_subsystem_initialized = TRUE;
2392
2393     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2394     {
2395       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2396       return;
2397     }
2398   }
2399
2400   for (i = 0; i < MAX_PLAYERS; i++)
2401   {
2402     /* get configured joystick for this player */
2403     char *device_name = setup.input[i].joy.device_name;
2404     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2405
2406     if (joystick_nr >= SDL_NumJoysticks())
2407     {
2408       if (setup.input[i].use_joystick && print_warning)
2409         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2410
2411       joystick_nr = -1;
2412     }
2413
2414     /* misuse joystick file descriptor variable to store joystick number */
2415     joystick.fd[i] = joystick_nr;
2416
2417     if (joystick_nr == -1)
2418       continue;
2419
2420     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2421     if (SDLCheckJoystickOpened(joystick_nr))
2422       SDLCloseJoystick(joystick_nr);
2423
2424     if (!setup.input[i].use_joystick)
2425       continue;
2426
2427     if (!SDLOpenJoystick(joystick_nr))
2428     {
2429       if (print_warning)
2430         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2431
2432       continue;
2433     }
2434
2435     joystick.status = JOYSTICK_ACTIVATED;
2436   }
2437 }
2438
2439 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2440 {
2441   if (nr < 0 || nr >= MAX_PLAYERS)
2442     return FALSE;
2443
2444   if (x != NULL)
2445     *x = sdl_js_axis[nr][0];
2446   if (y != NULL)
2447     *y = sdl_js_axis[nr][1];
2448
2449   if (b1 != NULL)
2450     *b1 = sdl_js_button[nr][0];
2451   if (b2 != NULL)
2452     *b2 = sdl_js_button[nr][1];
2453
2454   return TRUE;
2455 }