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