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