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