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