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