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