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