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