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