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