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