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