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