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