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