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