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