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