rnd-20131203-1-src
[rocksndiamonds.git] / src / libgame / sdl.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * sdl.c                                                    *
12 ***********************************************************/
13
14 #include "system.h"
15 #include "sound.h"
16 #include "joystick.h"
17 #include "misc.h"
18 #include "setup.h"
19
20
21 #if defined(TARGET_SDL)
22
23 /* ========================================================================= */
24 /* video functions                                                           */
25 /* ========================================================================= */
26
27 /* SDL internal variables */
28 #if defined(TARGET_SDL2)
29 // static SDL_Window *sdl_window = NULL;
30 SDL_Window *sdl_window = NULL;
31 // static SDL_Renderer *sdl_renderer = NULL;
32 #endif
33
34 /* stuff needed to work around SDL/Windows fullscreen drawing bug */
35 static int fullscreen_width;
36 static int fullscreen_height;
37 static int fullscreen_xoffset;
38 static int fullscreen_yoffset;
39 static int video_xoffset;
40 static int video_yoffset;
41
42 /* functions from SGE library */
43 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
44
45 static void setFullscreenParameters(char *fullscreen_mode_string)
46 {
47   struct ScreenModeInfo *fullscreen_mode;
48   int i;
49
50   fullscreen_mode = get_screen_mode_from_string(fullscreen_mode_string);
51
52   if (fullscreen_mode == NULL)
53     return;
54
55   for (i = 0; video.fullscreen_modes[i].width != -1; i++)
56   {
57     if (fullscreen_mode->width  == video.fullscreen_modes[i].width &&
58         fullscreen_mode->height == video.fullscreen_modes[i].height)
59     {
60       fullscreen_width  = fullscreen_mode->width;
61       fullscreen_height = fullscreen_mode->height;
62
63       fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
64       fullscreen_yoffset = (fullscreen_height - video.height) / 2;
65
66       break;
67     }
68   }
69 }
70
71 static void SDLSetWindowIcon(char *basename)
72 {
73   /* (setting the window icon on Mac OS X would replace the high-quality
74      dock icon with the currently smaller (and uglier) icon from file) */
75
76 #if !defined(PLATFORM_MACOSX)
77   char *filename = getCustomImageFilename(basename);
78   SDL_Surface *surface;
79
80   if (filename == NULL)
81   {
82     Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
83
84     return;
85   }
86
87   if ((surface = IMG_Load(filename)) == NULL)
88   {
89     Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
90
91     return;
92   }
93
94   /* set transparent color */
95   SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
96                   SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
97
98 #if defined(TARGET_SDL2)
99   SDL_SetWindowIcon(sdl_window, surface);
100 #else
101   SDL_WM_SetIcon(surface, NULL);
102 #endif
103 #endif
104 }
105
106 #if defined(TARGET_SDL2)
107 SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface)
108 {
109   if (backbuffer == NULL ||
110       backbuffer->surface == NULL)
111     return NULL;
112
113   return SDL_ConvertSurface(surface, backbuffer->surface->format, 0);
114 }
115 #endif
116
117 void SDLInitVideoDisplay(void)
118 {
119 #if !defined(TARGET_SDL2)
120   if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
121     SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
122
123   SDL_putenv("SDL_VIDEO_CENTERED=1");
124 #endif
125
126   /* initialize SDL video */
127   if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
128     Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
129
130   /* set default SDL depth */
131 #if !defined(TARGET_SDL2)
132   video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
133 #else
134   video.default_depth = 32;     // (how to determine video depth in SDL2?)
135 #endif
136 }
137
138 void SDLInitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
139                         boolean fullscreen)
140 {
141   static int screen_xy[][2] =
142   {
143     {  640, 480 },
144     {  800, 600 },
145     { 1024, 768 },
146     {   -1,  -1 }
147   };
148   SDL_Rect **modes;
149   int i, j;
150
151   /* default: normal game window size */
152   fullscreen_width = video.width;
153   fullscreen_height = video.height;
154   fullscreen_xoffset = 0;
155   fullscreen_yoffset = 0;
156
157   for (i = 0; screen_xy[i][0] != -1; i++)
158   {
159     if (screen_xy[i][0] >= video.width && screen_xy[i][1] >= video.height)
160     {
161       fullscreen_width  = screen_xy[i][0];
162       fullscreen_height = screen_xy[i][1];
163
164       break;
165     }
166   }
167
168   fullscreen_xoffset = (fullscreen_width  - video.width)  / 2;
169   fullscreen_yoffset = (fullscreen_height - video.height) / 2;
170
171 #if 1
172   checked_free(video.fullscreen_modes);
173
174   video.fullscreen_modes = NULL;
175   video.fullscreen_mode_current = NULL;
176 #endif
177
178 #if !defined(TARGET_SDL2)
179   /* get available hardware supported fullscreen modes */
180   modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
181 #else
182   // (for now, no display modes in SDL2 -- change this later)
183   modes = NULL;
184 #endif
185
186   if (modes == NULL)
187   {
188     /* no screen modes available => no fullscreen mode support */
189     video.fullscreen_available = FALSE;
190   }
191   else if (modes == (SDL_Rect **)-1)
192   {
193     /* fullscreen resolution is not restricted -- all resolutions available */
194     video.fullscreen_modes = checked_calloc(2 * sizeof(struct ScreenModeInfo));
195
196     /* use native video buffer size for fullscreen mode */
197     video.fullscreen_modes[0].width  = video.width;
198     video.fullscreen_modes[0].height = video.height;
199
200     video.fullscreen_modes[1].width  = -1;
201     video.fullscreen_modes[1].height = -1;
202   }
203   else
204   {
205     /* in this case, a certain number of screen modes is available */
206     int num_modes = 0;
207
208     for(i = 0; modes[i] != NULL; i++)
209     {
210       boolean found_mode = FALSE;
211
212       /* screen mode is smaller than video buffer size -- skip it */
213       if (modes[i]->w < video.width || modes[i]->h < video.height)
214         continue;
215
216       if (video.fullscreen_modes != NULL)
217         for (j = 0; video.fullscreen_modes[j].width != -1; j++)
218           if (modes[i]->w == video.fullscreen_modes[j].width &&
219               modes[i]->h == video.fullscreen_modes[j].height)
220             found_mode = TRUE;
221
222       if (found_mode)           /* screen mode already stored -- skip it */
223         continue;
224
225       /* new mode found; add it to list of available fullscreen modes */
226
227       num_modes++;
228
229       video.fullscreen_modes = checked_realloc(video.fullscreen_modes,
230                                                (num_modes + 1) *
231                                                sizeof(struct ScreenModeInfo));
232
233       video.fullscreen_modes[num_modes - 1].width  = modes[i]->w;
234       video.fullscreen_modes[num_modes - 1].height = modes[i]->h;
235
236       video.fullscreen_modes[num_modes].width  = -1;
237       video.fullscreen_modes[num_modes].height = -1;
238     }
239
240     if (num_modes == 0)
241     {
242       /* no appropriate screen modes available => no fullscreen mode support */
243       video.fullscreen_available = FALSE;
244     }
245   }
246
247   /* set window icon */
248   SDLSetWindowIcon(program.sdl_icon_filename);
249
250   /* open SDL video output device (window or fullscreen mode) */
251   if (!SDLSetVideoMode(backbuffer, fullscreen))
252     Error(ERR_EXIT, "setting video mode failed");
253
254   /* set window and icon title */
255 #if defined(TARGET_SDL2)
256   SDL_SetWindowTitle(sdl_window, program.window_title);
257 #else
258   SDL_WM_SetCaption(program.window_title, program.window_title);
259 #endif
260
261   /* SDL cannot directly draw to the visible video framebuffer like X11,
262      but always uses a backbuffer, which is then blitted to the visible
263      video framebuffer with 'SDL_UpdateRect' (or replaced with the current
264      visible video framebuffer with 'SDL_Flip', if the hardware supports
265      this). Therefore do not use an additional backbuffer for drawing, but
266      use a symbolic buffer (distinguishable from the SDL backbuffer) called
267      'window', which indicates that the SDL backbuffer should be updated to
268      the visible video framebuffer when attempting to blit to it.
269
270      For convenience, it seems to be a good idea to create this symbolic
271      buffer 'window' at the same size as the SDL backbuffer. Although it
272      should never be drawn to directly, it would do no harm nevertheless. */
273
274   /* create additional (symbolic) buffer for double-buffering */
275 #if 1
276   ReCreateBitmap(window, video.width, video.height, video.depth);
277 #else
278   *window = CreateBitmap(video.width, video.height, video.depth);
279 #endif
280 }
281
282 boolean SDLSetVideoMode(DrawBuffer **backbuffer, boolean fullscreen)
283 {
284   boolean success = TRUE;
285 #if defined(TARGET_SDL2)
286   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN;
287   int surface_flags_window = SURFACE_FLAGS;
288 #else
289   int surface_flags_fullscreen = SURFACE_FLAGS | SDL_FULLSCREEN;
290   int surface_flags_window = SURFACE_FLAGS;
291 #endif
292   SDL_Surface *new_surface = NULL;
293
294   if (*backbuffer == NULL)
295     *backbuffer = CreateBitmapStruct();
296
297   /* (real bitmap might be larger in fullscreen mode with video offsets) */
298   (*backbuffer)->width  = video.width;
299   (*backbuffer)->height = video.height;
300
301   if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
302   {
303     setFullscreenParameters(setup.fullscreen_mode);
304
305     video_xoffset = fullscreen_xoffset;
306     video_yoffset = fullscreen_yoffset;
307
308     /* switch display to fullscreen mode, if available */
309 #if defined(TARGET_SDL2)
310     sdl_window = SDL_CreateWindow(program.window_title,
311                                   SDL_WINDOWPOS_CENTERED,
312                                   SDL_WINDOWPOS_CENTERED,
313                                   fullscreen_width, fullscreen_height,
314                                   surface_flags_fullscreen);
315     if (sdl_window != NULL)
316     {
317       new_surface = SDL_GetWindowSurface(sdl_window);
318
319       SDL_UpdateWindowSurface(sdl_window);      // immediately map window
320     }
321 #else
322     new_surface = SDL_SetVideoMode(fullscreen_width, fullscreen_height,
323                                    video.depth, surface_flags_fullscreen);
324 #endif
325
326     if (new_surface == NULL)
327     {
328       /* switching display to fullscreen mode failed */
329       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
330
331       /* do not try it again */
332       video.fullscreen_available = FALSE;
333
334       success = FALSE;
335     }
336     else
337     {
338       (*backbuffer)->surface = new_surface;
339
340       video.fullscreen_enabled = TRUE;
341       video.fullscreen_mode_current = setup.fullscreen_mode;
342
343       success = TRUE;
344     }
345   }
346
347   if ((!fullscreen && video.fullscreen_enabled) || new_surface == NULL)
348   {
349     video_xoffset = 0;
350     video_yoffset = 0;
351
352     /* switch display to window mode */
353 #if defined(TARGET_SDL2)
354     sdl_window = SDL_CreateWindow(program.window_title,
355                                   SDL_WINDOWPOS_CENTERED,
356                                   SDL_WINDOWPOS_CENTERED,
357                                   video.width, video.height,
358                                   surface_flags_window);
359     if (sdl_window != NULL)
360     {
361       new_surface = SDL_GetWindowSurface(sdl_window);
362
363       SDL_UpdateWindowSurface(sdl_window);      // immediately map window
364     }
365 #else
366     new_surface = SDL_SetVideoMode(video.width, video.height,
367                                    video.depth, surface_flags_window);
368 #endif
369
370     if (new_surface == NULL)
371     {
372       /* switching display to window mode failed -- should not happen */
373       Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
374
375       success = FALSE;
376     }
377     else
378     {
379       (*backbuffer)->surface = new_surface;
380
381       video.fullscreen_enabled = FALSE;
382
383       success = TRUE;
384     }
385   }
386
387 #if 1
388   SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
389
390 #if defined(PLATFORM_WIN32)
391   {
392     SDL_SysWMinfo wminfo;
393     HWND hwnd;
394
395     SDL_VERSION(&wminfo.version);
396     SDL_GetWMInfo(&wminfo);
397
398     hwnd = wminfo.window;
399
400     DragAcceptFiles(hwnd, TRUE);
401   }
402 #endif
403 #endif
404
405
406   return success;
407 }
408
409 void SDLCreateBitmapContent(Bitmap *new_bitmap, int width, int height,
410                             int depth)
411 {
412   SDL_Surface *surface_tmp, *surface_native;
413
414   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth,
415                                           0, 0, 0, 0))
416       == NULL)
417     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
418
419   if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
420     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
421
422   SDL_FreeSurface(surface_tmp);
423
424   new_bitmap->surface = surface_native;
425 }
426
427 void SDLFreeBitmapPointers(Bitmap *bitmap)
428 {
429   if (bitmap->surface)
430     SDL_FreeSurface(bitmap->surface);
431   if (bitmap->surface_masked)
432     SDL_FreeSurface(bitmap->surface_masked);
433   bitmap->surface = NULL;
434   bitmap->surface_masked = NULL;
435 }
436
437 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
438                  int src_x, int src_y, int width, int height,
439                  int dst_x, int dst_y, int mask_mode)
440 {
441   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
442   SDL_Rect src_rect, dst_rect;
443
444   if (src_bitmap == backbuffer)
445   {
446     src_x += video_xoffset;
447     src_y += video_yoffset;
448   }
449
450   src_rect.x = src_x;
451   src_rect.y = src_y;
452   src_rect.w = width;
453   src_rect.h = height;
454
455   if (dst_bitmap == backbuffer || dst_bitmap == window)
456   {
457     dst_x += video_xoffset;
458     dst_y += video_yoffset;
459   }
460
461   dst_rect.x = dst_x;
462   dst_rect.y = dst_y;
463   dst_rect.w = width;
464   dst_rect.h = height;
465
466   if (src_bitmap != backbuffer || dst_bitmap != window)
467     SDL_BlitSurface((mask_mode == BLIT_MASKED ?
468                      src_bitmap->surface_masked : src_bitmap->surface),
469                     &src_rect, real_dst_bitmap->surface, &dst_rect);
470
471 #if defined(TARGET_SDL2)
472   if (dst_bitmap == window)
473     SDL_UpdateWindowSurface(sdl_window);
474 #else
475   if (dst_bitmap == window)
476     SDL_UpdateRect(backbuffer->surface, dst_x, dst_y, width, height);
477 #endif
478 }
479
480 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
481                       Uint32 color)
482 {
483   Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
484   SDL_Rect rect;
485
486   if (dst_bitmap == backbuffer || dst_bitmap == window)
487   {
488     x += video_xoffset;
489     y += video_yoffset;
490   }
491
492   rect.x = x;
493   rect.y = y;
494   rect.w = width;
495   rect.h = height;
496
497   SDL_FillRect(real_dst_bitmap->surface, &rect, color);
498
499 #if defined(TARGET_SDL2)
500   if (dst_bitmap == window)
501     SDL_UpdateWindowSurface(sdl_window);
502 #else
503   if (dst_bitmap == window)
504     SDL_UpdateRect(backbuffer->surface, x, y, width, height);
505 #endif
506 }
507
508 void SDLFadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
509                       int fade_mode, int fade_delay, int post_delay,
510                       void (*draw_border_function)(void))
511 {
512   static boolean initialization_needed = TRUE;
513   static SDL_Surface *surface_source = NULL;
514   static SDL_Surface *surface_target = NULL;
515   static SDL_Surface *surface_black = NULL;
516   SDL_Surface *surface_screen = backbuffer->surface;
517   SDL_Surface *surface_cross = (bitmap_cross ? bitmap_cross->surface : NULL);
518   SDL_Rect src_rect, dst_rect;
519   int src_x = x, src_y = y;
520   int dst_x = x, dst_y = y;
521   unsigned int time_last, time_current;
522
523   /* check if screen size has changed */
524   if (surface_source != NULL && (video.width  != surface_source->w ||
525                                  video.height != surface_source->h))
526   {
527     SDL_FreeSurface(surface_source);
528     SDL_FreeSurface(surface_target);
529     SDL_FreeSurface(surface_black);
530
531     initialization_needed = TRUE;
532   }
533
534   src_rect.x = src_x;
535   src_rect.y = src_y;
536   src_rect.w = width;
537   src_rect.h = height;
538
539   dst_x += video_xoffset;
540   dst_y += video_yoffset;
541
542   dst_rect.x = dst_x;
543   dst_rect.y = dst_y;
544   dst_rect.w = width;           /* (ignored) */
545   dst_rect.h = height;          /* (ignored) */
546
547   if (initialization_needed)
548   {
549 #if defined(TARGET_SDL2)
550     unsigned int flags = 0;
551 #else
552     unsigned int flags = SDL_SRCALPHA;
553
554     /* use same surface type as screen surface */
555     if ((surface_screen->flags & SDL_HWSURFACE))
556       flags |= SDL_HWSURFACE;
557     else
558       flags |= SDL_SWSURFACE;
559 #endif
560
561     /* create surface for temporary copy of screen buffer (source) */
562     if ((surface_source =
563          SDL_CreateRGBSurface(flags,
564                               video.width,
565                               video.height,
566                               surface_screen->format->BitsPerPixel,
567                               surface_screen->format->Rmask,
568                               surface_screen->format->Gmask,
569                               surface_screen->format->Bmask,
570                               surface_screen->format->Amask)) == NULL)
571       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
572
573     /* create surface for cross-fading screen buffer (target) */
574     if ((surface_target =
575          SDL_CreateRGBSurface(flags,
576                               video.width,
577                               video.height,
578                               surface_screen->format->BitsPerPixel,
579                               surface_screen->format->Rmask,
580                               surface_screen->format->Gmask,
581                               surface_screen->format->Bmask,
582                               surface_screen->format->Amask)) == NULL)
583       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
584
585     /* create black surface for fading from/to black */
586     if ((surface_black =
587          SDL_CreateRGBSurface(flags,
588                               video.width,
589                               video.height,
590                               surface_screen->format->BitsPerPixel,
591                               surface_screen->format->Rmask,
592                               surface_screen->format->Gmask,
593                               surface_screen->format->Bmask,
594                               surface_screen->format->Amask)) == NULL)
595       Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
596
597     /* completely fill the surface with black color pixels */
598     SDL_FillRect(surface_black, NULL,
599                  SDL_MapRGB(surface_screen->format, 0, 0, 0));
600
601     initialization_needed = FALSE;
602   }
603
604   /* copy source and target surfaces to temporary surfaces for fading */
605   if (fade_mode & FADE_TYPE_TRANSFORM)
606   {
607     SDL_BlitSurface(surface_cross,  &src_rect, surface_source, &src_rect);
608     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
609   }
610   else if (fade_mode & FADE_TYPE_FADE_IN)
611   {
612     SDL_BlitSurface(surface_black,  &src_rect, surface_source, &src_rect);
613     SDL_BlitSurface(surface_screen, &dst_rect, surface_target, &src_rect);
614   }
615   else          /* FADE_TYPE_FADE_OUT */
616   {
617     SDL_BlitSurface(surface_screen, &dst_rect, surface_source, &src_rect);
618     SDL_BlitSurface(surface_black,  &src_rect, surface_target, &src_rect);
619   }
620
621   time_current = SDL_GetTicks();
622
623   if (fade_mode == FADE_MODE_MELT)
624   {
625     boolean done = FALSE;
626     int melt_pixels = 2;
627     int melt_columns = width / melt_pixels;
628     int ypos[melt_columns];
629     int max_steps = height / 8 + 32;
630     int steps_done = 0;
631     float steps = 0;
632     int i;
633
634     SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
635 #if defined(TARGET_SDL2)
636     SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_NONE);
637 #else
638     SDL_SetAlpha(surface_target, 0, 0);         /* disable alpha blending */
639 #endif
640
641     ypos[0] = -GetSimpleRandom(16);
642
643     for (i = 1 ; i < melt_columns; i++)
644     {
645       int r = GetSimpleRandom(3) - 1;   /* randomly choose from { -1, 0, -1 } */
646
647       ypos[i] = ypos[i - 1] + r;
648
649       if (ypos[i] > 0)
650         ypos[i] = 0;
651       else
652         if (ypos[i] == -16)
653           ypos[i] = -15;
654     }
655
656     while (!done)
657     {
658       int steps_final;
659
660       time_last = time_current;
661       time_current = SDL_GetTicks();
662       steps += max_steps * ((float)(time_current - time_last) / fade_delay);
663       steps_final = MIN(MAX(0, steps), max_steps);
664
665       steps_done++;
666
667       done = (steps_done >= steps_final);
668
669       for (i = 0 ; i < melt_columns; i++)
670       {
671         if (ypos[i] < 0)
672         {
673           ypos[i]++;
674
675           done = FALSE;
676         }
677         else if (ypos[i] < height)
678         {
679           int y1 = 16;
680           int y2 = 8;
681           int y3 = 8;
682           int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
683
684           if (ypos[i] + dy >= height)
685             dy = height - ypos[i];
686
687           /* copy part of (appearing) target surface to upper area */
688           src_rect.x = src_x + i * melt_pixels;
689           // src_rect.y = src_y + ypos[i];
690           src_rect.y = src_y;
691           src_rect.w = melt_pixels;
692           // src_rect.h = dy;
693           src_rect.h = ypos[i] + dy;
694
695           dst_rect.x = dst_x + i * melt_pixels;
696           // dst_rect.y = dst_y + ypos[i];
697           dst_rect.y = dst_y;
698
699           if (steps_done >= steps_final)
700             SDL_BlitSurface(surface_target, &src_rect,
701                             surface_screen, &dst_rect);
702
703           ypos[i] += dy;
704
705           /* copy part of (disappearing) source surface to lower area */
706           src_rect.x = src_x + i * melt_pixels;
707           src_rect.y = src_y;
708           src_rect.w = melt_pixels;
709           src_rect.h = height - ypos[i];
710
711           dst_rect.x = dst_x + i * melt_pixels;
712           dst_rect.y = dst_y + ypos[i];
713
714           if (steps_done >= steps_final)
715             SDL_BlitSurface(surface_source, &src_rect,
716                             surface_screen, &dst_rect);
717
718           done = FALSE;
719         }
720         else
721         {
722           src_rect.x = src_x + i * melt_pixels;
723           src_rect.y = src_y;
724           src_rect.w = melt_pixels;
725           src_rect.h = height;
726
727           dst_rect.x = dst_x + i * melt_pixels;
728           dst_rect.y = dst_y;
729
730           if (steps_done >= steps_final)
731             SDL_BlitSurface(surface_target, &src_rect,
732                             surface_screen, &dst_rect);
733         }
734       }
735
736       if (steps_done >= steps_final)
737       {
738         if (draw_border_function != NULL)
739           draw_border_function();
740
741 #if defined(TARGET_SDL2)
742         SDL_UpdateWindowSurface(sdl_window);
743 #else
744         SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
745 #endif
746       }
747     }
748   }
749   else
750   {
751     float alpha;
752     int alpha_final;
753
754     for (alpha = 0.0; alpha < 255.0;)
755     {
756       time_last = time_current;
757       time_current = SDL_GetTicks();
758       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
759       alpha_final = MIN(MAX(0, alpha), 255);
760
761       /* draw existing (source) image to screen buffer */
762       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
763
764       /* draw new (target) image to screen buffer using alpha blending */
765 #if defined(TARGET_SDL2)
766       SDL_SetSurfaceAlphaMod(surface_target, alpha_final);
767       SDL_SetSurfaceBlendMode(surface_target, SDL_BLENDMODE_BLEND);
768 #else
769       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
770 #endif
771       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
772
773       if (draw_border_function != NULL)
774         draw_border_function();
775
776 #if 1
777       /* only update the region of the screen that is affected from fading */
778 #if defined(TARGET_SDL2)
779       SDL_UpdateWindowSurface(sdl_window);
780 #else
781       SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
782 #endif
783 #else
784       SDL_Flip(surface_screen);
785 #endif
786     }
787   }
788
789   Delay(post_delay);
790 }
791
792 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
793                        int to_x, int to_y, Uint32 color)
794 {
795   SDL_Surface *surface = dst_bitmap->surface;
796   SDL_Rect rect;
797
798   if (from_x > to_x)
799     swap_numbers(&from_x, &to_x);
800
801   if (from_y > to_y)
802     swap_numbers(&from_y, &to_y);
803
804   rect.x = from_x;
805   rect.y = from_y;
806   rect.w = (to_x - from_x + 1);
807   rect.h = (to_y - from_y + 1);
808
809   if (dst_bitmap == backbuffer || dst_bitmap == window)
810   {
811     rect.x += video_xoffset;
812     rect.y += video_yoffset;
813   }
814
815   SDL_FillRect(surface, &rect, color);
816 }
817
818 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
819                  int to_x, int to_y, Uint32 color)
820 {
821   if (dst_bitmap == backbuffer || dst_bitmap == window)
822   {
823     from_x += video_xoffset;
824     from_y += video_yoffset;
825     to_x += video_xoffset;
826     to_y += video_yoffset;
827   }
828
829   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
830 }
831
832 #if 0
833 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
834                   int num_points, Uint32 color)
835 {
836   int i, x, y;
837   int line_width = 4;
838
839   for (i = 0; i < num_points - 1; i++)
840   {
841     for (x = 0; x < line_width; x++)
842     {
843       for (y = 0; y < line_width; y++)
844       {
845         int dx = x - line_width / 2;
846         int dy = y - line_width / 2;
847
848         if ((x == 0 && y == 0) ||
849             (x == 0 && y == line_width - 1) ||
850             (x == line_width - 1 && y == 0) ||
851             (x == line_width - 1 && y == line_width - 1))
852           continue;
853
854         sge_Line(surface, points[i].x + dx, points[i].y + dy,
855                  points[i+1].x + dx, points[i+1].y + dy, color);
856       }
857     }
858   }
859 }
860 #endif
861
862 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
863 {
864   SDL_Surface *surface = src_bitmap->surface;
865
866   if (src_bitmap == backbuffer || src_bitmap == window)
867   {
868     x += video_xoffset;
869     y += video_yoffset;
870   }
871
872   switch (surface->format->BytesPerPixel)
873   {
874     case 1:             /* assuming 8-bpp */
875     {
876       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
877     }
878     break;
879
880     case 2:             /* probably 15-bpp or 16-bpp */
881     {
882       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
883     }
884     break;
885
886   case 3:               /* slow 24-bpp mode; usually not used */
887     {
888       /* does this work? */
889       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
890       Uint32 color = 0;
891       int shift;
892
893       shift = surface->format->Rshift;
894       color |= *(pix + shift / 8) >> shift;
895       shift = surface->format->Gshift;
896       color |= *(pix + shift / 8) >> shift;
897       shift = surface->format->Bshift;
898       color |= *(pix + shift / 8) >> shift;
899
900       return color;
901     }
902     break;
903
904   case 4:               /* probably 32-bpp */
905     {
906       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
907     }
908     break;
909   }
910
911   return 0;
912 }
913
914
915 /* ========================================================================= */
916 /* The following functions were taken from the SGE library                   */
917 /* (SDL Graphics Extension Library) by Anders Lindström                      */
918 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
919 /* ========================================================================= */
920
921 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
922 {
923   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
924   {
925     switch (surface->format->BytesPerPixel)
926     {
927       case 1:
928       {
929         /* Assuming 8-bpp */
930         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
931       }
932       break;
933
934       case 2:
935       {
936         /* Probably 15-bpp or 16-bpp */
937         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
938       }
939       break;
940
941       case 3:
942       {
943         /* Slow 24-bpp mode, usually not used */
944         Uint8 *pix;
945         int shift;
946
947         /* Gack - slow, but endian correct */
948         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
949         shift = surface->format->Rshift;
950         *(pix+shift/8) = color>>shift;
951         shift = surface->format->Gshift;
952         *(pix+shift/8) = color>>shift;
953         shift = surface->format->Bshift;
954         *(pix+shift/8) = color>>shift;
955       }
956       break;
957
958       case 4:
959       {
960         /* Probably 32-bpp */
961         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
962       }
963       break;
964     }
965   }
966 }
967
968 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
969                   Uint8 R, Uint8 G, Uint8 B)
970 {
971   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
972 }
973
974 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
975 {
976   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
977 }
978
979 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
980 {
981   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
982 }
983
984 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
985 {
986   Uint8 *pix;
987   int shift;
988
989   /* Gack - slow, but endian correct */
990   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
991   shift = surface->format->Rshift;
992   *(pix+shift/8) = color>>shift;
993   shift = surface->format->Gshift;
994   *(pix+shift/8) = color>>shift;
995   shift = surface->format->Bshift;
996   *(pix+shift/8) = color>>shift;
997 }
998
999 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1000 {
1001   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1002 }
1003
1004 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1005 {
1006   switch (dest->format->BytesPerPixel)
1007   {
1008     case 1:
1009       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1010       break;
1011
1012     case 2:
1013       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1014       break;
1015
1016     case 3:
1017       _PutPixel24(dest,x,y,color);
1018       break;
1019
1020     case 4:
1021       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1022       break;
1023   }
1024 }
1025
1026 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1027 {
1028   if (SDL_MUSTLOCK(surface))
1029   {
1030     if (SDL_LockSurface(surface) < 0)
1031     {
1032       return;
1033     }
1034   }
1035
1036   _PutPixel(surface, x, y, color);
1037
1038   if (SDL_MUSTLOCK(surface))
1039   {
1040     SDL_UnlockSurface(surface);
1041   }
1042 }
1043
1044 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1045                   Uint8 r, Uint8 g, Uint8 b)
1046 {
1047   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1048 }
1049
1050 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1051 {
1052   if (y >= 0 && y <= dest->h - 1)
1053   {
1054     switch (dest->format->BytesPerPixel)
1055     {
1056       case 1:
1057         return y*dest->pitch;
1058         break;
1059
1060       case 2:
1061         return y*dest->pitch/2;
1062         break;
1063
1064       case 3:
1065         return y*dest->pitch;
1066         break;
1067
1068       case 4:
1069         return y*dest->pitch/4;
1070         break;
1071     }
1072   }
1073
1074   return -1;
1075 }
1076
1077 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1078 {
1079   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1080   {
1081     switch (surface->format->BytesPerPixel)
1082     {
1083       case 1:
1084       {
1085         /* Assuming 8-bpp */
1086         *((Uint8 *)surface->pixels + ypitch + x) = color;
1087       }
1088       break;
1089
1090       case 2:
1091       {
1092         /* Probably 15-bpp or 16-bpp */
1093         *((Uint16 *)surface->pixels + ypitch + x) = color;
1094       }
1095       break;
1096
1097       case 3:
1098       {
1099         /* Slow 24-bpp mode, usually not used */
1100         Uint8 *pix;
1101         int shift;
1102
1103         /* Gack - slow, but endian correct */
1104         pix = (Uint8 *)surface->pixels + ypitch + x*3;
1105         shift = surface->format->Rshift;
1106         *(pix+shift/8) = color>>shift;
1107         shift = surface->format->Gshift;
1108         *(pix+shift/8) = color>>shift;
1109         shift = surface->format->Bshift;
1110         *(pix+shift/8) = color>>shift;
1111       }
1112       break;
1113
1114       case 4:
1115       {
1116         /* Probably 32-bpp */
1117         *((Uint32 *)surface->pixels + ypitch + x) = color;
1118       }
1119       break;
1120     }
1121   }
1122 }
1123
1124 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1125                Uint32 Color)
1126 {
1127   SDL_Rect l;
1128
1129   if (SDL_MUSTLOCK(Surface))
1130   {
1131     if (SDL_LockSurface(Surface) < 0)
1132     {
1133       return;
1134     }
1135   }
1136
1137   if (x1 > x2)
1138   {
1139     Sint16 tmp = x1;
1140     x1 = x2;
1141     x2 = tmp;
1142   }
1143
1144   /* Do the clipping */
1145   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1146     return;
1147   if (x1 < 0)
1148     x1 = 0;
1149   if (x2 > Surface->w - 1)
1150     x2 = Surface->w - 1;
1151
1152   l.x = x1;
1153   l.y = y;
1154   l.w = x2 - x1 + 1;
1155   l.h = 1;
1156
1157   SDL_FillRect(Surface, &l, Color);
1158
1159   if (SDL_MUSTLOCK(Surface))
1160   {
1161     SDL_UnlockSurface(Surface);
1162   }
1163 }
1164
1165 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1166                   Uint8 R, Uint8 G, Uint8 B)
1167 {
1168   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1169 }
1170
1171 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1172 {
1173   SDL_Rect l;
1174
1175   if (x1 > x2)
1176   {
1177     Sint16 tmp = x1;
1178     x1 = x2;
1179     x2 = tmp;
1180   }
1181
1182   /* Do the clipping */
1183   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1184     return;
1185   if (x1 < 0)
1186     x1 = 0;
1187   if (x2 > Surface->w - 1)
1188     x2 = Surface->w - 1;
1189
1190   l.x = x1;
1191   l.y = y;
1192   l.w = x2 - x1 + 1;
1193   l.h = 1;
1194
1195   SDL_FillRect(Surface, &l, Color);
1196 }
1197
1198 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1199                Uint32 Color)
1200 {
1201   SDL_Rect l;
1202
1203   if (SDL_MUSTLOCK(Surface))
1204   {
1205     if (SDL_LockSurface(Surface) < 0)
1206     {
1207       return;
1208     }
1209   }
1210
1211   if (y1 > y2)
1212   {
1213     Sint16 tmp = y1;
1214     y1 = y2;
1215     y2 = tmp;
1216   }
1217
1218   /* Do the clipping */
1219   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1220     return;
1221   if (y1 < 0)
1222     y1 = 0;
1223   if (y2 > Surface->h - 1)
1224     y2 = Surface->h - 1;
1225
1226   l.x = x;
1227   l.y = y1;
1228   l.w = 1;
1229   l.h = y2 - y1 + 1;
1230
1231   SDL_FillRect(Surface, &l, Color);
1232
1233   if (SDL_MUSTLOCK(Surface))
1234   {
1235     SDL_UnlockSurface(Surface);
1236   }
1237 }
1238
1239 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1240                   Uint8 R, Uint8 G, Uint8 B)
1241 {
1242   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1243 }
1244
1245 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1246 {
1247   SDL_Rect l;
1248
1249   if (y1 > y2)
1250   {
1251     Sint16 tmp = y1;
1252     y1 = y2;
1253     y2 = tmp;
1254   }
1255
1256   /* Do the clipping */
1257   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1258     return;
1259   if (y1 < 0)
1260     y1 = 0;
1261   if (y2 > Surface->h - 1)
1262     y2 = Surface->h - 1;
1263
1264   l.x = x;
1265   l.y = y1;
1266   l.w = 1;
1267   l.h = y2 - y1 + 1;
1268
1269   SDL_FillRect(Surface, &l, Color);
1270 }
1271
1272 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1273                 Sint16 x2, Sint16 y2, Uint32 Color,
1274                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1275                               Uint32 Color))
1276 {
1277   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1278
1279   dx = x2 - x1;
1280   dy = y2 - y1;
1281
1282   sdx = (dx < 0) ? -1 : 1;
1283   sdy = (dy < 0) ? -1 : 1;
1284
1285   dx = sdx * dx + 1;
1286   dy = sdy * dy + 1;
1287
1288   x = y = 0;
1289
1290   px = x1;
1291   py = y1;
1292
1293   if (dx >= dy)
1294   {
1295     for (x = 0; x < dx; x++)
1296     {
1297       Callback(Surface, px, py, Color);
1298
1299       y += dy;
1300       if (y >= dx)
1301       {
1302         y -= dx;
1303         py += sdy;
1304       }
1305
1306       px += sdx;
1307     }
1308   }
1309   else
1310   {
1311     for (y = 0; y < dy; y++)
1312     {
1313       Callback(Surface, px, py, Color);
1314
1315       x += dx;
1316       if (x >= dy)
1317       {
1318         x -= dy;
1319         px += sdx;
1320       }
1321
1322       py += sdy;
1323     }
1324   }
1325 }
1326
1327 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1328                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1329                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1330                                  Uint32 Color))
1331 {
1332   sge_DoLine(Surface, X1, Y1, X2, Y2,
1333              SDL_MapRGB(Surface->format, R, G, B), Callback);
1334 }
1335
1336 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1337               Uint32 Color)
1338 {
1339   if (SDL_MUSTLOCK(Surface))
1340   {
1341     if (SDL_LockSurface(Surface) < 0)
1342       return;
1343    }
1344
1345    /* Draw the line */
1346    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1347
1348    /* unlock the display */
1349    if (SDL_MUSTLOCK(Surface))
1350    {
1351       SDL_UnlockSurface(Surface);
1352    }
1353 }
1354
1355 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1356                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1357 {
1358   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1359 }
1360
1361 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1362 {
1363   if (dst_bitmap == backbuffer || dst_bitmap == window)
1364   {
1365     x += video_xoffset;
1366     y += video_yoffset;
1367   }
1368
1369   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1370 }
1371
1372
1373 /*
1374   -----------------------------------------------------------------------------
1375   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1376   -----------------------------------------------------------------------------
1377 */
1378
1379 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1380                    int width, int height, Uint32 color)
1381 {
1382   int x, y;
1383
1384   for (y = src_y; y < src_y + height; y++)
1385   {
1386     for (x = src_x; x < src_x + width; x++)
1387     {
1388       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1389
1390       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1391     }
1392   }
1393 }
1394
1395 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1396                           int src_x, int src_y, int width, int height,
1397                           int dst_x, int dst_y)
1398 {
1399   int x, y;
1400
1401   for (y = 0; y < height; y++)
1402   {
1403     for (x = 0; x < width; x++)
1404     {
1405       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1406
1407       if (pixel != BLACK_PIXEL)
1408         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1409     }
1410   }
1411 }
1412
1413
1414 /* ========================================================================= */
1415 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1416 /* (Rotozoomer) by Andreas Schiffler                                         */
1417 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1418 /* ========================================================================= */
1419
1420 /*
1421   -----------------------------------------------------------------------------
1422   32 bit zoomer
1423
1424   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1425   -----------------------------------------------------------------------------
1426 */
1427
1428 typedef struct
1429 {
1430   Uint8 r;
1431   Uint8 g;
1432   Uint8 b;
1433   Uint8 a;
1434 } tColorRGBA;
1435
1436 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1437 {
1438   int x, y;
1439   tColorRGBA *sp, *csp, *dp;
1440 #if 0
1441   int sgap;
1442 #endif
1443   int dgap;
1444
1445   /* pointer setup */
1446   sp = csp = (tColorRGBA *) src->pixels;
1447   dp = (tColorRGBA *) dst->pixels;
1448 #if 0
1449   sgap = src->pitch - src->w * 4;
1450 #endif
1451   dgap = dst->pitch - dst->w * 4;
1452
1453   for (y = 0; y < dst->h; y++)
1454   {
1455     sp = csp;
1456
1457     for (x = 0; x < dst->w; x++)
1458     {
1459       tColorRGBA *sp0 = sp;
1460       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1461       tColorRGBA *sp00 = &sp0[0];
1462       tColorRGBA *sp01 = &sp0[1];
1463       tColorRGBA *sp10 = &sp1[0];
1464       tColorRGBA *sp11 = &sp1[1];
1465       tColorRGBA new;
1466
1467       /* create new color pixel from all four source color pixels */
1468       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1469       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1470       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1471       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1472
1473       /* draw */
1474       *dp = new;
1475
1476       /* advance source pointers */
1477       sp += 2;
1478
1479       /* advance destination pointer */
1480       dp++;
1481     }
1482
1483     /* advance source pointer */
1484     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1485
1486     /* advance destination pointers */
1487     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1488   }
1489
1490   return 0;
1491 }
1492
1493 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1494 {
1495   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1496   tColorRGBA *sp, *csp, *dp;
1497 #if 0
1498   int sgap;
1499 #endif
1500   int dgap;
1501
1502   /* use specialized zoom function when scaling down to exactly half size */
1503   if (src->w == 2 * dst->w &&
1504       src->h == 2 * dst->h)
1505     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1506
1507   /* variable setup */
1508   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1509   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1510
1511   /* allocate memory for row increments */
1512   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1513   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1514
1515   /* precalculate row increments */
1516   csx = 0;
1517   csax = sax;
1518   for (x = 0; x <= dst->w; x++)
1519   {
1520     *csax = csx;
1521     csax++;
1522     csx &= 0xffff;
1523     csx += sx;
1524   }
1525
1526   csy = 0;
1527   csay = say;
1528   for (y = 0; y <= dst->h; y++)
1529   {
1530     *csay = csy;
1531     csay++;
1532     csy &= 0xffff;
1533     csy += sy;
1534   }
1535
1536   /* pointer setup */
1537   sp = csp = (tColorRGBA *) src->pixels;
1538   dp = (tColorRGBA *) dst->pixels;
1539 #if 0
1540   sgap = src->pitch - src->w * 4;
1541 #endif
1542   dgap = dst->pitch - dst->w * 4;
1543
1544   csay = say;
1545   for (y = 0; y < dst->h; y++)
1546   {
1547     sp = csp;
1548     csax = sax;
1549
1550     for (x = 0; x < dst->w; x++)
1551     {
1552       /* draw */
1553       *dp = *sp;
1554
1555       /* advance source pointers */
1556       csax++;
1557       sp += (*csax >> 16);
1558
1559       /* advance destination pointer */
1560       dp++;
1561     }
1562
1563     /* advance source pointer */
1564     csay++;
1565     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1566
1567     /* advance destination pointers */
1568     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1569   }
1570
1571   free(sax);
1572   free(say);
1573
1574   return 0;
1575 }
1576
1577 /*
1578   -----------------------------------------------------------------------------
1579   8 bit zoomer
1580
1581   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1582   -----------------------------------------------------------------------------
1583 */
1584
1585 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1586 {
1587   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1588   Uint8 *sp, *dp, *csp;
1589   int dgap;
1590
1591   /* variable setup */
1592   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1593   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1594
1595   /* allocate memory for row increments */
1596   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1597   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1598
1599   /* precalculate row increments */
1600   csx = 0;
1601   csax = sax;
1602   for (x = 0; x < dst->w; x++)
1603   {
1604     csx += sx;
1605     *csax = (csx >> 16);
1606     csx &= 0xffff;
1607     csax++;
1608   }
1609
1610   csy = 0;
1611   csay = say;
1612   for (y = 0; y < dst->h; y++)
1613   {
1614     csy += sy;
1615     *csay = (csy >> 16);
1616     csy &= 0xffff;
1617     csay++;
1618   }
1619
1620   csx = 0;
1621   csax = sax;
1622   for (x = 0; x < dst->w; x++)
1623   {
1624     csx += (*csax);
1625     csax++;
1626   }
1627
1628   csy = 0;
1629   csay = say;
1630   for (y = 0; y < dst->h; y++)
1631   {
1632     csy += (*csay);
1633     csay++;
1634   }
1635
1636   /* pointer setup */
1637   sp = csp = (Uint8 *) src->pixels;
1638   dp = (Uint8 *) dst->pixels;
1639   dgap = dst->pitch - dst->w;
1640
1641   /* draw */
1642   csay = say;
1643   for (y = 0; y < dst->h; y++)
1644   {
1645     csax = sax;
1646     sp = csp;
1647     for (x = 0; x < dst->w; x++)
1648     {
1649       /* draw */
1650       *dp = *sp;
1651
1652       /* advance source pointers */
1653       sp += (*csax);
1654       csax++;
1655
1656       /* advance destination pointer */
1657       dp++;
1658     }
1659
1660     /* advance source pointer (for row) */
1661     csp += ((*csay) * src->pitch);
1662     csay++;
1663
1664     /* advance destination pointers */
1665     dp += dgap;
1666   }
1667
1668   free(sax);
1669   free(say);
1670
1671   return 0;
1672 }
1673
1674 /*
1675   -----------------------------------------------------------------------------
1676   zoomSurface()
1677
1678   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1679   'zoomx' and 'zoomy' are scaling factors for width and height.
1680   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1681   into a 32bit RGBA format on the fly.
1682   -----------------------------------------------------------------------------
1683 */
1684
1685 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1686 {
1687   SDL_Surface *zoom_src = NULL;
1688   SDL_Surface *zoom_dst = NULL;
1689   boolean is_converted = FALSE;
1690   boolean is_32bit;
1691   int i;
1692
1693   if (src == NULL)
1694     return NULL;
1695
1696   /* determine if source surface is 32 bit or 8 bit */
1697   is_32bit = (src->format->BitsPerPixel == 32);
1698
1699   if (is_32bit || src->format->BitsPerPixel == 8)
1700   {
1701     /* use source surface 'as is' */
1702     zoom_src = src;
1703   }
1704   else
1705   {
1706     /* new source surface is 32 bit with a defined RGB ordering */
1707     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1708                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1709     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1710     is_32bit = TRUE;
1711     is_converted = TRUE;
1712   }
1713
1714   /* allocate surface to completely contain the zoomed surface */
1715   if (is_32bit)
1716   {
1717     /* target surface is 32 bit with source RGBA/ABGR ordering */
1718     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1719                                     zoom_src->format->Rmask,
1720                                     zoom_src->format->Gmask,
1721                                     zoom_src->format->Bmask, 0);
1722   }
1723   else
1724   {
1725     /* target surface is 8 bit */
1726     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1727                                     0, 0, 0, 0);
1728   }
1729
1730   /* lock source surface */
1731   SDL_LockSurface(zoom_src);
1732
1733   /* check which kind of surface we have */
1734   if (is_32bit)
1735   {
1736     /* call the 32 bit transformation routine to do the zooming */
1737     zoomSurfaceRGBA(zoom_src, zoom_dst);
1738   }
1739   else
1740   {
1741     /* copy palette */
1742     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1743       zoom_dst->format->palette->colors[i] =
1744         zoom_src->format->palette->colors[i];
1745     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1746
1747     /* call the 8 bit transformation routine to do the zooming */
1748     zoomSurfaceY(zoom_src, zoom_dst);
1749   }
1750
1751   /* unlock source surface */
1752   SDL_UnlockSurface(zoom_src);
1753
1754   /* free temporary surface */
1755   if (is_converted)
1756     SDL_FreeSurface(zoom_src);
1757
1758   /* return destination surface */
1759   return zoom_dst;
1760 }
1761
1762 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1763 {
1764   SDL_Surface *sdl_surface_tmp;
1765   int dst_width = dst_bitmap->width;
1766   int dst_height = dst_bitmap->height;
1767
1768   /* throw away old destination surface */
1769   SDL_FreeSurface(dst_bitmap->surface);
1770
1771   /* create zoomed temporary surface from source surface */
1772   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1773
1774   /* create native format destination surface from zoomed temporary surface */
1775   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1776
1777   /* free temporary surface */
1778   SDL_FreeSurface(sdl_surface_tmp);
1779 }
1780
1781
1782 /* ========================================================================= */
1783 /* load image to bitmap                                                      */
1784 /* ========================================================================= */
1785
1786 Bitmap *SDLLoadImage(char *filename)
1787 {
1788   Bitmap *new_bitmap = CreateBitmapStruct();
1789   SDL_Surface *sdl_image_tmp;
1790
1791   /* load image to temporary surface */
1792   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1793   {
1794     SetError("IMG_Load(): %s", SDL_GetError());
1795
1796     return NULL;
1797   }
1798
1799   UPDATE_BUSY_STATE();
1800
1801   /* create native non-transparent surface for current image */
1802   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1803   {
1804     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1805
1806     return NULL;
1807   }
1808
1809   UPDATE_BUSY_STATE();
1810
1811   /* create native transparent surface for current image */
1812   SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
1813                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1814   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1815   {
1816     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1817
1818     return NULL;
1819   }
1820
1821   UPDATE_BUSY_STATE();
1822
1823   /* free temporary surface */
1824   SDL_FreeSurface(sdl_image_tmp);
1825
1826   new_bitmap->width = new_bitmap->surface->w;
1827   new_bitmap->height = new_bitmap->surface->h;
1828
1829   return new_bitmap;
1830 }
1831
1832
1833 /* ------------------------------------------------------------------------- */
1834 /* custom cursor fuctions                                                    */
1835 /* ------------------------------------------------------------------------- */
1836
1837 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1838 {
1839   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1840                           cursor_info->width, cursor_info->height,
1841                           cursor_info->hot_x, cursor_info->hot_y);
1842 }
1843
1844 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1845 {
1846   static struct MouseCursorInfo *last_cursor_info = NULL;
1847   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1848   static SDL_Cursor *cursor_default = NULL;
1849   static SDL_Cursor *cursor_current = NULL;
1850
1851   /* if invoked for the first time, store the SDL default cursor */
1852   if (cursor_default == NULL)
1853     cursor_default = SDL_GetCursor();
1854
1855   /* only create new cursor if cursor info (custom only) has changed */
1856   if (cursor_info != NULL && cursor_info != last_cursor_info)
1857   {
1858     cursor_current = create_cursor(cursor_info);
1859     last_cursor_info = cursor_info;
1860   }
1861
1862   /* only set new cursor if cursor info (custom or NULL) has changed */
1863   if (cursor_info != last_cursor_info2)
1864     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1865
1866   last_cursor_info2 = cursor_info;
1867 }
1868
1869
1870 /* ========================================================================= */
1871 /* audio functions                                                           */
1872 /* ========================================================================= */
1873
1874 void SDLOpenAudio(void)
1875 {
1876 #if !defined(TARGET_SDL2)
1877   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1878     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1879 #endif
1880
1881   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1882   {
1883     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1884     return;
1885   }
1886
1887   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1888                     AUDIO_NUM_CHANNELS_STEREO,
1889                     setup.system.audio_fragment_size) < 0)
1890   {
1891     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1892     return;
1893   }
1894
1895   audio.sound_available = TRUE;
1896   audio.music_available = TRUE;
1897   audio.loops_available = TRUE;
1898   audio.sound_enabled = TRUE;
1899
1900   /* set number of available mixer channels */
1901   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1902   audio.music_channel = MUSIC_CHANNEL;
1903   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1904
1905   Mixer_InitChannels();
1906 }
1907
1908 void SDLCloseAudio(void)
1909 {
1910   Mix_HaltMusic();
1911   Mix_HaltChannel(-1);
1912
1913   Mix_CloseAudio();
1914   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1915 }
1916
1917
1918 /* ========================================================================= */
1919 /* event functions                                                           */
1920 /* ========================================================================= */
1921
1922 void SDLNextEvent(Event *event)
1923 {
1924   SDL_WaitEvent(event);
1925
1926   if (event->type == EVENT_BUTTONPRESS ||
1927       event->type == EVENT_BUTTONRELEASE)
1928   {
1929     if (((ButtonEvent *)event)->x > video_xoffset)
1930       ((ButtonEvent *)event)->x -= video_xoffset;
1931     else
1932       ((ButtonEvent *)event)->x = 0;
1933     if (((ButtonEvent *)event)->y > video_yoffset)
1934       ((ButtonEvent *)event)->y -= video_yoffset;
1935     else
1936       ((ButtonEvent *)event)->y = 0;
1937   }
1938   else if (event->type == EVENT_MOTIONNOTIFY)
1939   {
1940     if (((MotionEvent *)event)->x > video_xoffset)
1941       ((MotionEvent *)event)->x -= video_xoffset;
1942     else
1943       ((MotionEvent *)event)->x = 0;
1944     if (((MotionEvent *)event)->y > video_yoffset)
1945       ((MotionEvent *)event)->y -= video_yoffset;
1946     else
1947       ((MotionEvent *)event)->y = 0;
1948   }
1949 }
1950
1951 void SDLHandleWindowManagerEvent(Event *event)
1952 {
1953 #if defined(PLATFORM_WIN32)
1954   SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
1955   SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
1956
1957   if (syswmmsg->msg == WM_DROPFILES)
1958   {
1959     HDROP hdrop = (HDROP)syswmmsg->wParam;
1960     int i, num_files;
1961
1962     printf("::: SDL_SYSWMEVENT:\n");
1963
1964     num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
1965
1966     for (i = 0; i < num_files; i++)
1967     {
1968       int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
1969       char buffer[buffer_len + 1];
1970
1971       DragQueryFile(hdrop, i, buffer, buffer_len + 1);
1972
1973       printf("::: - '%s'\n", buffer);
1974     }
1975
1976     DragFinish((HDROP)syswmmsg->wParam);
1977   }
1978 #endif
1979 }
1980
1981
1982 /* ========================================================================= */
1983 /* joystick functions                                                        */
1984 /* ========================================================================= */
1985
1986 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1987 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1988 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1989
1990 static boolean SDLOpenJoystick(int nr)
1991 {
1992   if (nr < 0 || nr > MAX_PLAYERS)
1993     return FALSE;
1994
1995   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1996 }
1997
1998 static void SDLCloseJoystick(int nr)
1999 {
2000   if (nr < 0 || nr > MAX_PLAYERS)
2001     return;
2002
2003   SDL_JoystickClose(sdl_joystick[nr]);
2004
2005   sdl_joystick[nr] = NULL;
2006 }
2007
2008 static boolean SDLCheckJoystickOpened(int nr)
2009 {
2010   if (nr < 0 || nr > MAX_PLAYERS)
2011     return FALSE;
2012
2013 #if defined(TARGET_SDL2)
2014   return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2015 #else
2016   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2017 #endif
2018 }
2019
2020 void HandleJoystickEvent(Event *event)
2021 {
2022   switch(event->type)
2023   {
2024     case SDL_JOYAXISMOTION:
2025       if (event->jaxis.axis < 2)
2026         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2027       break;
2028
2029     case SDL_JOYBUTTONDOWN:
2030       if (event->jbutton.button < 2)
2031         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2032       break;
2033
2034     case SDL_JOYBUTTONUP:
2035       if (event->jbutton.button < 2)
2036         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2037       break;
2038
2039     default:
2040       break;
2041   }
2042 }
2043
2044 void SDLInitJoysticks()
2045 {
2046   static boolean sdl_joystick_subsystem_initialized = FALSE;
2047   boolean print_warning = !sdl_joystick_subsystem_initialized;
2048   int i;
2049
2050   if (!sdl_joystick_subsystem_initialized)
2051   {
2052     sdl_joystick_subsystem_initialized = TRUE;
2053
2054     if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2055     {
2056       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2057       return;
2058     }
2059   }
2060
2061   for (i = 0; i < MAX_PLAYERS; i++)
2062   {
2063     /* get configured joystick for this player */
2064     char *device_name = setup.input[i].joy.device_name;
2065     int joystick_nr = getJoystickNrFromDeviceName(device_name);
2066
2067     if (joystick_nr >= SDL_NumJoysticks())
2068     {
2069       if (setup.input[i].use_joystick && print_warning)
2070         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2071
2072       joystick_nr = -1;
2073     }
2074
2075     /* misuse joystick file descriptor variable to store joystick number */
2076     joystick.fd[i] = joystick_nr;
2077
2078     if (joystick_nr == -1)
2079       continue;
2080
2081     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2082     if (SDLCheckJoystickOpened(joystick_nr))
2083       SDLCloseJoystick(joystick_nr);
2084
2085     if (!setup.input[i].use_joystick)
2086       continue;
2087
2088     if (!SDLOpenJoystick(joystick_nr))
2089     {
2090       if (print_warning)
2091         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2092
2093       continue;
2094     }
2095
2096     joystick.status = JOYSTICK_ACTIVATED;
2097   }
2098 }
2099
2100 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2101 {
2102   if (nr < 0 || nr >= MAX_PLAYERS)
2103     return FALSE;
2104
2105   if (x != NULL)
2106     *x = sdl_js_axis[nr][0];
2107   if (y != NULL)
2108     *y = sdl_js_axis[nr][1];
2109
2110   if (b1 != NULL)
2111     *b1 = sdl_js_button[nr][0];
2112   if (b2 != NULL)
2113     *b2 = sdl_js_button[nr][1];
2114
2115   return TRUE;
2116 }
2117
2118 #endif /* TARGET_SDL */