rnd-20070427-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       {
604         if (draw_border_function != NULL)
605           draw_border_function();
606
607         SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
608       }
609     }
610   }
611   else
612   {
613     for (alpha = 0.0; alpha < 255.0;)
614     {
615       time_last = time_current;
616       time_current = SDL_GetTicks();
617       alpha += 255 * ((float)(time_current - time_last) / fade_delay);
618       alpha_final = MIN(MAX(0, alpha), 255);
619
620       /* draw existing (source) image to screen buffer */
621       SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
622
623       /* draw new (target) image to screen buffer using alpha blending */
624       SDL_SetAlpha(surface_target, SDL_SRCALPHA, alpha_final);
625       SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
626
627       if (draw_border_function != NULL)
628         draw_border_function();
629
630 #if 1
631       /* only update the region of the screen that is affected from fading */
632       SDL_UpdateRect(surface_screen, dst_x, dst_y, width, height);
633 #else
634       SDL_Flip(surface_screen);
635 #endif
636     }
637   }
638
639   Delay(post_delay);
640 }
641
642 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
643                        int to_x, int to_y, Uint32 color)
644 {
645   SDL_Surface *surface = dst_bitmap->surface;
646   SDL_Rect rect;
647
648   if (from_x > to_x)
649     swap_numbers(&from_x, &to_x);
650
651   if (from_y > to_y)
652     swap_numbers(&from_y, &to_y);
653
654   rect.x = from_x;
655   rect.y = from_y;
656   rect.w = (to_x - from_x + 1);
657   rect.h = (to_y - from_y + 1);
658
659   if (dst_bitmap == backbuffer || dst_bitmap == window)
660   {
661     rect.x += video_xoffset;
662     rect.y += video_yoffset;
663   }
664
665   SDL_FillRect(surface, &rect, color);
666 }
667
668 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
669                  int to_x, int to_y, Uint32 color)
670 {
671   if (dst_bitmap == backbuffer || dst_bitmap == window)
672   {
673     from_x += video_xoffset;
674     from_y += video_yoffset;
675     to_x += video_xoffset;
676     to_y += video_yoffset;
677   }
678
679   sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
680 }
681
682 #if 0
683 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
684                   int num_points, Uint32 color)
685 {
686   int i, x, y;
687   int line_width = 4;
688
689   for (i = 0; i < num_points - 1; i++)
690   {
691     for (x = 0; x < line_width; x++)
692     {
693       for (y = 0; y < line_width; y++)
694       {
695         int dx = x - line_width / 2;
696         int dy = y - line_width / 2;
697
698         if ((x == 0 && y == 0) ||
699             (x == 0 && y == line_width - 1) ||
700             (x == line_width - 1 && y == 0) ||
701             (x == line_width - 1 && y == line_width - 1))
702           continue;
703
704         sge_Line(surface, points[i].x + dx, points[i].y + dy,
705                  points[i+1].x + dx, points[i+1].y + dy, color);
706       }
707     }
708   }
709 }
710 #endif
711
712 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
713 {
714   SDL_Surface *surface = src_bitmap->surface;
715
716   if (src_bitmap == backbuffer || src_bitmap == window)
717   {
718     x += video_xoffset;
719     y += video_yoffset;
720   }
721
722   switch (surface->format->BytesPerPixel)
723   {
724     case 1:             /* assuming 8-bpp */
725     {
726       return *((Uint8 *)surface->pixels + y * surface->pitch + x);
727     }
728     break;
729
730     case 2:             /* probably 15-bpp or 16-bpp */
731     {
732       return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
733     }
734     break;
735
736   case 3:               /* slow 24-bpp mode; usually not used */
737     {
738       /* does this work? */
739       Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
740       Uint32 color = 0;
741       int shift;
742
743       shift = surface->format->Rshift;
744       color |= *(pix + shift / 8) >> shift;
745       shift = surface->format->Gshift;
746       color |= *(pix + shift / 8) >> shift;
747       shift = surface->format->Bshift;
748       color |= *(pix + shift / 8) >> shift;
749
750       return color;
751     }
752     break;
753
754   case 4:               /* probably 32-bpp */
755     {
756       return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
757     }
758     break;
759   }
760
761   return 0;
762 }
763
764
765 /* ========================================================================= */
766 /* The following functions were taken from the SGE library                   */
767 /* (SDL Graphics Extension Library) by Anders Lindström                      */
768 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html                        */
769 /* ========================================================================= */
770
771 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
772 {
773   if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
774   {
775     switch (surface->format->BytesPerPixel)
776     {
777       case 1:
778       {
779         /* Assuming 8-bpp */
780         *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
781       }
782       break;
783
784       case 2:
785       {
786         /* Probably 15-bpp or 16-bpp */
787         *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
788       }
789       break;
790
791       case 3:
792       {
793         /* Slow 24-bpp mode, usually not used */
794         Uint8 *pix;
795         int shift;
796
797         /* Gack - slow, but endian correct */
798         pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
799         shift = surface->format->Rshift;
800         *(pix+shift/8) = color>>shift;
801         shift = surface->format->Gshift;
802         *(pix+shift/8) = color>>shift;
803         shift = surface->format->Bshift;
804         *(pix+shift/8) = color>>shift;
805       }
806       break;
807
808       case 4:
809       {
810         /* Probably 32-bpp */
811         *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
812       }
813       break;
814     }
815   }
816 }
817
818 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
819                   Uint8 R, Uint8 G, Uint8 B)
820 {
821   _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
822 }
823
824 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
825 {
826   *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
827 }
828
829 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
830 {
831   *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
832 }
833
834 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
835 {
836   Uint8 *pix;
837   int shift;
838
839   /* Gack - slow, but endian correct */
840   pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
841   shift = surface->format->Rshift;
842   *(pix+shift/8) = color>>shift;
843   shift = surface->format->Gshift;
844   *(pix+shift/8) = color>>shift;
845   shift = surface->format->Bshift;
846   *(pix+shift/8) = color>>shift;
847 }
848
849 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
850 {
851   *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
852 }
853
854 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
855 {
856   switch (dest->format->BytesPerPixel)
857   {
858     case 1:
859       *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
860       break;
861
862     case 2:
863       *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
864       break;
865
866     case 3:
867       _PutPixel24(dest,x,y,color);
868       break;
869
870     case 4:
871       *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
872       break;
873   }
874 }
875
876 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
877 {
878   if (SDL_MUSTLOCK(surface))
879   {
880     if (SDL_LockSurface(surface) < 0)
881     {
882       return;
883     }
884   }
885
886   _PutPixel(surface, x, y, color);
887
888   if (SDL_MUSTLOCK(surface))
889   {
890     SDL_UnlockSurface(surface);
891   }
892 }
893
894 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
895                   Uint8 r, Uint8 g, Uint8 b)
896 {
897   sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
898 }
899
900 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
901 {
902   if (y >= 0 && y <= dest->h - 1)
903   {
904     switch (dest->format->BytesPerPixel)
905     {
906       case 1:
907         return y*dest->pitch;
908         break;
909
910       case 2:
911         return y*dest->pitch/2;
912         break;
913
914       case 3:
915         return y*dest->pitch;
916         break;
917
918       case 4:
919         return y*dest->pitch/4;
920         break;
921     }
922   }
923
924   return -1;
925 }
926
927 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
928 {
929   if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
930   {
931     switch (surface->format->BytesPerPixel)
932     {
933       case 1:
934       {
935         /* Assuming 8-bpp */
936         *((Uint8 *)surface->pixels + ypitch + x) = color;
937       }
938       break;
939
940       case 2:
941       {
942         /* Probably 15-bpp or 16-bpp */
943         *((Uint16 *)surface->pixels + ypitch + x) = color;
944       }
945       break;
946
947       case 3:
948       {
949         /* Slow 24-bpp mode, usually not used */
950         Uint8 *pix;
951         int shift;
952
953         /* Gack - slow, but endian correct */
954         pix = (Uint8 *)surface->pixels + ypitch + x*3;
955         shift = surface->format->Rshift;
956         *(pix+shift/8) = color>>shift;
957         shift = surface->format->Gshift;
958         *(pix+shift/8) = color>>shift;
959         shift = surface->format->Bshift;
960         *(pix+shift/8) = color>>shift;
961       }
962       break;
963
964       case 4:
965       {
966         /* Probably 32-bpp */
967         *((Uint32 *)surface->pixels + ypitch + x) = color;
968       }
969       break;
970     }
971   }
972 }
973
974 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
975                Uint32 Color)
976 {
977   SDL_Rect l;
978
979   if (SDL_MUSTLOCK(Surface))
980   {
981     if (SDL_LockSurface(Surface) < 0)
982     {
983       return;
984     }
985   }
986
987   if (x1 > x2)
988   {
989     Sint16 tmp = x1;
990     x1 = x2;
991     x2 = tmp;
992   }
993
994   /* Do the clipping */
995   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
996     return;
997   if (x1 < 0)
998     x1 = 0;
999   if (x2 > Surface->w - 1)
1000     x2 = Surface->w - 1;
1001
1002   l.x = x1;
1003   l.y = y;
1004   l.w = x2 - x1 + 1;
1005   l.h = 1;
1006
1007   SDL_FillRect(Surface, &l, Color);
1008
1009   if (SDL_MUSTLOCK(Surface))
1010   {
1011     SDL_UnlockSurface(Surface);
1012   }
1013 }
1014
1015 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1016                   Uint8 R, Uint8 G, Uint8 B)
1017 {
1018   sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1019 }
1020
1021 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1022 {
1023   SDL_Rect l;
1024
1025   if (x1 > x2)
1026   {
1027     Sint16 tmp = x1;
1028     x1 = x2;
1029     x2 = tmp;
1030   }
1031
1032   /* Do the clipping */
1033   if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1034     return;
1035   if (x1 < 0)
1036     x1 = 0;
1037   if (x2 > Surface->w - 1)
1038     x2 = Surface->w - 1;
1039
1040   l.x = x1;
1041   l.y = y;
1042   l.w = x2 - x1 + 1;
1043   l.h = 1;
1044
1045   SDL_FillRect(Surface, &l, Color);
1046 }
1047
1048 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1049                Uint32 Color)
1050 {
1051   SDL_Rect l;
1052
1053   if (SDL_MUSTLOCK(Surface))
1054   {
1055     if (SDL_LockSurface(Surface) < 0)
1056     {
1057       return;
1058     }
1059   }
1060
1061   if (y1 > y2)
1062   {
1063     Sint16 tmp = y1;
1064     y1 = y2;
1065     y2 = tmp;
1066   }
1067
1068   /* Do the clipping */
1069   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1070     return;
1071   if (y1 < 0)
1072     y1 = 0;
1073   if (y2 > Surface->h - 1)
1074     y2 = Surface->h - 1;
1075
1076   l.x = x;
1077   l.y = y1;
1078   l.w = 1;
1079   l.h = y2 - y1 + 1;
1080
1081   SDL_FillRect(Surface, &l, Color);
1082
1083   if (SDL_MUSTLOCK(Surface))
1084   {
1085     SDL_UnlockSurface(Surface);
1086   }
1087 }
1088
1089 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1090                   Uint8 R, Uint8 G, Uint8 B)
1091 {
1092   sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1093 }
1094
1095 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1096 {
1097   SDL_Rect l;
1098
1099   if (y1 > y2)
1100   {
1101     Sint16 tmp = y1;
1102     y1 = y2;
1103     y2 = tmp;
1104   }
1105
1106   /* Do the clipping */
1107   if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1108     return;
1109   if (y1 < 0)
1110     y1 = 0;
1111   if (y2 > Surface->h - 1)
1112     y2 = Surface->h - 1;
1113
1114   l.x = x;
1115   l.y = y1;
1116   l.w = 1;
1117   l.h = y2 - y1 + 1;
1118
1119   SDL_FillRect(Surface, &l, Color);
1120 }
1121
1122 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1123                 Sint16 x2, Sint16 y2, Uint32 Color,
1124                 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1125                               Uint32 Color))
1126 {
1127   Sint16 dx, dy, sdx, sdy, x, y, px, py;
1128
1129   dx = x2 - x1;
1130   dy = y2 - y1;
1131
1132   sdx = (dx < 0) ? -1 : 1;
1133   sdy = (dy < 0) ? -1 : 1;
1134
1135   dx = sdx * dx + 1;
1136   dy = sdy * dy + 1;
1137
1138   x = y = 0;
1139
1140   px = x1;
1141   py = y1;
1142
1143   if (dx >= dy)
1144   {
1145     for (x = 0; x < dx; x++)
1146     {
1147       Callback(Surface, px, py, Color);
1148
1149       y += dy;
1150       if (y >= dx)
1151       {
1152         y -= dx;
1153         py += sdy;
1154       }
1155
1156       px += sdx;
1157     }
1158   }
1159   else
1160   {
1161     for (y = 0; y < dy; y++)
1162     {
1163       Callback(Surface, px, py, Color);
1164
1165       x += dx;
1166       if (x >= dy)
1167       {
1168         x -= dy;
1169         px += sdx;
1170       }
1171
1172       py += sdy;
1173     }
1174   }
1175 }
1176
1177 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1178                    Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1179                    void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1180                                  Uint32 Color))
1181 {
1182   sge_DoLine(Surface, X1, Y1, X2, Y2,
1183              SDL_MapRGB(Surface->format, R, G, B), Callback);
1184 }
1185
1186 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1187               Uint32 Color)
1188 {
1189   if (SDL_MUSTLOCK(Surface))
1190   {
1191     if (SDL_LockSurface(Surface) < 0)
1192       return;
1193    }
1194
1195    /* Draw the line */
1196    sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1197
1198    /* unlock the display */
1199    if (SDL_MUSTLOCK(Surface))
1200    {
1201       SDL_UnlockSurface(Surface);
1202    }
1203 }
1204
1205 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1206                  Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1207 {
1208   sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1209 }
1210
1211 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1212 {
1213   if (dst_bitmap == backbuffer || dst_bitmap == window)
1214   {
1215     x += video_xoffset;
1216     y += video_yoffset;
1217   }
1218
1219   sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1220 }
1221
1222
1223 /*
1224   -----------------------------------------------------------------------------
1225   quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1226   -----------------------------------------------------------------------------
1227 */
1228
1229 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1230                    int width, int height, Uint32 color)
1231 {
1232   int x, y;
1233
1234   for (y = src_y; y < src_y + height; y++)
1235   {
1236     for (x = src_x; x < src_x + width; x++)
1237     {
1238       Uint32 pixel = SDLGetPixel(bitmap, x, y);
1239
1240       SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1241     }
1242   }
1243 }
1244
1245 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1246                           int src_x, int src_y, int width, int height,
1247                           int dst_x, int dst_y)
1248 {
1249   int x, y;
1250
1251   for (y = 0; y < height; y++)
1252   {
1253     for (x = 0; x < width; x++)
1254     {
1255       Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1256
1257       if (pixel != BLACK_PIXEL)
1258         SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1259     }
1260   }
1261 }
1262
1263
1264 /* ========================================================================= */
1265 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1266 /* (Rotozoomer) by Andreas Schiffler                                         */
1267 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html                   */
1268 /* ========================================================================= */
1269
1270 /*
1271   -----------------------------------------------------------------------------
1272   32 bit zoomer
1273
1274   zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1275   -----------------------------------------------------------------------------
1276 */
1277
1278 typedef struct
1279 {
1280   Uint8 r;
1281   Uint8 g;
1282   Uint8 b;
1283   Uint8 a;
1284 } tColorRGBA;
1285
1286 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1287 {
1288   int x, y;
1289   tColorRGBA *sp, *csp, *dp;
1290   int sgap, dgap;
1291
1292   /* pointer setup */
1293   sp = csp = (tColorRGBA *) src->pixels;
1294   dp = (tColorRGBA *) dst->pixels;
1295   sgap = src->pitch - src->w * 4;
1296   dgap = dst->pitch - dst->w * 4;
1297
1298   for (y = 0; y < dst->h; y++)
1299   {
1300     sp = csp;
1301
1302     for (x = 0; x < dst->w; x++)
1303     {
1304       tColorRGBA *sp0 = sp;
1305       tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1306       tColorRGBA *sp00 = &sp0[0];
1307       tColorRGBA *sp01 = &sp0[1];
1308       tColorRGBA *sp10 = &sp1[0];
1309       tColorRGBA *sp11 = &sp1[1];
1310       tColorRGBA new;
1311
1312       /* create new color pixel from all four source color pixels */
1313       new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1314       new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1315       new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1316       new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1317
1318       /* draw */
1319       *dp = new;
1320
1321       /* advance source pointers */
1322       sp += 2;
1323
1324       /* advance destination pointer */
1325       dp++;
1326     }
1327
1328     /* advance source pointer */
1329     csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1330
1331     /* advance destination pointers */
1332     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1333   }
1334
1335   return 0;
1336 }
1337
1338 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1339 {
1340   int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1341   tColorRGBA *sp, *csp, *dp;
1342   int sgap, dgap;
1343
1344   /* use specialized zoom function when scaling down to exactly half size */
1345   if (src->w == 2 * dst->w &&
1346       src->h == 2 * dst->h)
1347     return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1348
1349   /* variable setup */
1350   sx = (int) (65536.0 * (float) src->w / (float) dst->w);
1351   sy = (int) (65536.0 * (float) src->h / (float) dst->h);
1352
1353   /* allocate memory for row increments */
1354   sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1355   say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1356
1357   /* precalculate row increments */
1358   csx = 0;
1359   csax = sax;
1360   for (x = 0; x <= dst->w; x++)
1361   {
1362     *csax = csx;
1363     csax++;
1364     csx &= 0xffff;
1365     csx += sx;
1366   }
1367
1368   csy = 0;
1369   csay = say;
1370   for (y = 0; y <= dst->h; y++)
1371   {
1372     *csay = csy;
1373     csay++;
1374     csy &= 0xffff;
1375     csy += sy;
1376   }
1377
1378   /* pointer setup */
1379   sp = csp = (tColorRGBA *) src->pixels;
1380   dp = (tColorRGBA *) dst->pixels;
1381   sgap = src->pitch - src->w * 4;
1382   dgap = dst->pitch - dst->w * 4;
1383
1384   csay = say;
1385   for (y = 0; y < dst->h; y++)
1386   {
1387     sp = csp;
1388     csax = sax;
1389
1390     for (x = 0; x < dst->w; x++)
1391     {
1392       /* draw */
1393       *dp = *sp;
1394
1395       /* advance source pointers */
1396       csax++;
1397       sp += (*csax >> 16);
1398
1399       /* advance destination pointer */
1400       dp++;
1401     }
1402
1403     /* advance source pointer */
1404     csay++;
1405     csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
1406
1407     /* advance destination pointers */
1408     dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1409   }
1410
1411   free(sax);
1412   free(say);
1413
1414   return 0;
1415 }
1416
1417 /*
1418   -----------------------------------------------------------------------------
1419   8 bit zoomer
1420
1421   zoomes 8 bit palette/Y 'src' surface to 'dst' surface
1422   -----------------------------------------------------------------------------
1423 */
1424
1425 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
1426 {
1427   Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
1428   Uint8 *sp, *dp, *csp;
1429   int dgap;
1430
1431   /* variable setup */
1432   sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
1433   sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
1434
1435   /* allocate memory for row increments */
1436   sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
1437   say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
1438
1439   /* precalculate row increments */
1440   csx = 0;
1441   csax = sax;
1442   for (x = 0; x < dst->w; x++)
1443   {
1444     csx += sx;
1445     *csax = (csx >> 16);
1446     csx &= 0xffff;
1447     csax++;
1448   }
1449
1450   csy = 0;
1451   csay = say;
1452   for (y = 0; y < dst->h; y++)
1453   {
1454     csy += sy;
1455     *csay = (csy >> 16);
1456     csy &= 0xffff;
1457     csay++;
1458   }
1459
1460   csx = 0;
1461   csax = sax;
1462   for (x = 0; x < dst->w; x++)
1463   {
1464     csx += (*csax);
1465     csax++;
1466   }
1467
1468   csy = 0;
1469   csay = say;
1470   for (y = 0; y < dst->h; y++)
1471   {
1472     csy += (*csay);
1473     csay++;
1474   }
1475
1476   /* pointer setup */
1477   sp = csp = (Uint8 *) src->pixels;
1478   dp = (Uint8 *) dst->pixels;
1479   dgap = dst->pitch - dst->w;
1480
1481   /* draw */
1482   csay = say;
1483   for (y = 0; y < dst->h; y++)
1484   {
1485     csax = sax;
1486     sp = csp;
1487     for (x = 0; x < dst->w; x++)
1488     {
1489       /* draw */
1490       *dp = *sp;
1491
1492       /* advance source pointers */
1493       sp += (*csax);
1494       csax++;
1495
1496       /* advance destination pointer */
1497       dp++;
1498     }
1499
1500     /* advance source pointer (for row) */
1501     csp += ((*csay) * src->pitch);
1502     csay++;
1503
1504     /* advance destination pointers */
1505     dp += dgap;
1506   }
1507
1508   free(sax);
1509   free(say);
1510
1511   return 0;
1512 }
1513
1514 /*
1515   -----------------------------------------------------------------------------
1516   zoomSurface()
1517
1518   Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1519   'zoomx' and 'zoomy' are scaling factors for width and height.
1520   If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
1521   into a 32bit RGBA format on the fly.
1522   -----------------------------------------------------------------------------
1523 */
1524
1525 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
1526 {
1527   SDL_Surface *zoom_src = NULL;
1528   SDL_Surface *zoom_dst = NULL;
1529   boolean is_converted = FALSE;
1530   boolean is_32bit;
1531   int i;
1532
1533   if (src == NULL)
1534     return NULL;
1535
1536   /* determine if source surface is 32 bit or 8 bit */
1537   is_32bit = (src->format->BitsPerPixel == 32);
1538
1539   if (is_32bit || src->format->BitsPerPixel == 8)
1540   {
1541     /* use source surface 'as is' */
1542     zoom_src = src;
1543   }
1544   else
1545   {
1546     /* new source surface is 32 bit with a defined RGB ordering */
1547     zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1548                                     0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1549     SDL_BlitSurface(src, NULL, zoom_src, NULL);
1550     is_32bit = TRUE;
1551     is_converted = TRUE;
1552   }
1553
1554   /* allocate surface to completely contain the zoomed surface */
1555   if (is_32bit)
1556   {
1557     /* target surface is 32 bit with source RGBA/ABGR ordering */
1558     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 32,
1559                                     zoom_src->format->Rmask,
1560                                     zoom_src->format->Gmask,
1561                                     zoom_src->format->Bmask, 0);
1562   }
1563   else
1564   {
1565     /* target surface is 8 bit */
1566     zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_width, dst_height, 8,
1567                                     0, 0, 0, 0);
1568   }
1569
1570   /* lock source surface */
1571   SDL_LockSurface(zoom_src);
1572
1573   /* check which kind of surface we have */
1574   if (is_32bit)
1575   {
1576     /* call the 32 bit transformation routine to do the zooming */
1577     zoomSurfaceRGBA(zoom_src, zoom_dst);
1578   }
1579   else
1580   {
1581     /* copy palette */
1582     for (i = 0; i < zoom_src->format->palette->ncolors; i++)
1583       zoom_dst->format->palette->colors[i] =
1584         zoom_src->format->palette->colors[i];
1585     zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
1586
1587     /* call the 8 bit transformation routine to do the zooming */
1588     zoomSurfaceY(zoom_src, zoom_dst);
1589   }
1590
1591   /* unlock source surface */
1592   SDL_UnlockSurface(zoom_src);
1593
1594   /* free temporary surface */
1595   if (is_converted)
1596     SDL_FreeSurface(zoom_src);
1597
1598   /* return destination surface */
1599   return zoom_dst;
1600 }
1601
1602 void SDLZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
1603 {
1604   SDL_Surface *sdl_surface_tmp;
1605   int dst_width = dst_bitmap->width;
1606   int dst_height = dst_bitmap->height;
1607
1608   /* throw away old destination surface */
1609   SDL_FreeSurface(dst_bitmap->surface);
1610
1611   /* create zoomed temporary surface from source surface */
1612   sdl_surface_tmp = zoomSurface(src_bitmap->surface, dst_width, dst_height);
1613
1614   /* create native format destination surface from zoomed temporary surface */
1615   dst_bitmap->surface = SDL_DisplayFormat(sdl_surface_tmp);
1616
1617   /* free temporary surface */
1618   SDL_FreeSurface(sdl_surface_tmp);
1619 }
1620
1621
1622 /* ========================================================================= */
1623 /* load image to bitmap                                                      */
1624 /* ========================================================================= */
1625
1626 Bitmap *SDLLoadImage(char *filename)
1627 {
1628   Bitmap *new_bitmap = CreateBitmapStruct();
1629   SDL_Surface *sdl_image_tmp;
1630
1631   /* load image to temporary surface */
1632   if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
1633   {
1634     SetError("IMG_Load(): %s", SDL_GetError());
1635
1636     return NULL;
1637   }
1638
1639   UPDATE_BUSY_STATE();
1640
1641   /* create native non-transparent surface for current image */
1642   if ((new_bitmap->surface = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1643   {
1644     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1645
1646     return NULL;
1647   }
1648
1649   UPDATE_BUSY_STATE();
1650
1651   /* create native transparent surface for current image */
1652   SDL_SetColorKey(sdl_image_tmp, SDL_SRCCOLORKEY,
1653                   SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
1654   if ((new_bitmap->surface_masked = SDL_DisplayFormat(sdl_image_tmp)) == NULL)
1655   {
1656     SetError("SDL_DisplayFormat(): %s", SDL_GetError());
1657
1658     return NULL;
1659   }
1660
1661   UPDATE_BUSY_STATE();
1662
1663   /* free temporary surface */
1664   SDL_FreeSurface(sdl_image_tmp);
1665
1666   new_bitmap->width = new_bitmap->surface->w;
1667   new_bitmap->height = new_bitmap->surface->h;
1668
1669   return new_bitmap;
1670 }
1671
1672
1673 /* ------------------------------------------------------------------------- */
1674 /* custom cursor fuctions                                                    */
1675 /* ------------------------------------------------------------------------- */
1676
1677 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
1678 {
1679   return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
1680                           cursor_info->width, cursor_info->height,
1681                           cursor_info->hot_x, cursor_info->hot_y);
1682 }
1683
1684 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
1685 {
1686   static struct MouseCursorInfo *last_cursor_info = NULL;
1687   static struct MouseCursorInfo *last_cursor_info2 = NULL;
1688   static SDL_Cursor *cursor_default = NULL;
1689   static SDL_Cursor *cursor_current = NULL;
1690
1691   /* if invoked for the first time, store the SDL default cursor */
1692   if (cursor_default == NULL)
1693     cursor_default = SDL_GetCursor();
1694
1695   /* only create new cursor if cursor info (custom only) has changed */
1696   if (cursor_info != NULL && cursor_info != last_cursor_info)
1697   {
1698     cursor_current = create_cursor(cursor_info);
1699     last_cursor_info = cursor_info;
1700   }
1701
1702   /* only set new cursor if cursor info (custom or NULL) has changed */
1703   if (cursor_info != last_cursor_info2)
1704     SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
1705
1706   last_cursor_info2 = cursor_info;
1707 }
1708
1709
1710 /* ========================================================================= */
1711 /* audio functions                                                           */
1712 /* ========================================================================= */
1713
1714 void SDLOpenAudio(void)
1715 {
1716   if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
1717     SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
1718
1719   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
1720   {
1721     Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
1722     return;
1723   }
1724
1725   if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
1726                     AUDIO_NUM_CHANNELS_STEREO,
1727                     setup.system.audio_fragment_size) < 0)
1728   {
1729     Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
1730     return;
1731   }
1732
1733   audio.sound_available = TRUE;
1734   audio.music_available = TRUE;
1735   audio.loops_available = TRUE;
1736   audio.sound_enabled = TRUE;
1737
1738   /* set number of available mixer channels */
1739   audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
1740   audio.music_channel = MUSIC_CHANNEL;
1741   audio.first_sound_channel = FIRST_SOUND_CHANNEL;
1742
1743   Mixer_InitChannels();
1744 }
1745
1746 void SDLCloseAudio(void)
1747 {
1748   Mix_HaltMusic();
1749   Mix_HaltChannel(-1);
1750
1751   Mix_CloseAudio();
1752   SDL_QuitSubSystem(SDL_INIT_AUDIO);
1753 }
1754
1755
1756 /* ========================================================================= */
1757 /* event functions                                                           */
1758 /* ========================================================================= */
1759
1760 void SDLNextEvent(Event *event)
1761 {
1762   SDL_WaitEvent(event);
1763
1764   if (event->type == EVENT_BUTTONPRESS ||
1765       event->type == EVENT_BUTTONRELEASE)
1766   {
1767     if (((ButtonEvent *)event)->x > video_xoffset)
1768       ((ButtonEvent *)event)->x -= video_xoffset;
1769     else
1770       ((ButtonEvent *)event)->x = 0;
1771     if (((ButtonEvent *)event)->y > video_yoffset)
1772       ((ButtonEvent *)event)->y -= video_yoffset;
1773     else
1774       ((ButtonEvent *)event)->y = 0;
1775   }
1776   else if (event->type == EVENT_MOTIONNOTIFY)
1777   {
1778     if (((MotionEvent *)event)->x > video_xoffset)
1779       ((MotionEvent *)event)->x -= video_xoffset;
1780     else
1781       ((MotionEvent *)event)->x = 0;
1782     if (((MotionEvent *)event)->y > video_yoffset)
1783       ((MotionEvent *)event)->y -= video_yoffset;
1784     else
1785       ((MotionEvent *)event)->y = 0;
1786   }
1787 }
1788
1789
1790 /* ========================================================================= */
1791 /* joystick functions                                                        */
1792 /* ========================================================================= */
1793
1794 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
1795 static int sdl_js_axis[MAX_PLAYERS][2]   = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1796 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
1797
1798 static boolean SDLOpenJoystick(int nr)
1799 {
1800   if (nr < 0 || nr > MAX_PLAYERS)
1801     return FALSE;
1802
1803   return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
1804 }
1805
1806 static void SDLCloseJoystick(int nr)
1807 {
1808   if (nr < 0 || nr > MAX_PLAYERS)
1809     return;
1810
1811   SDL_JoystickClose(sdl_joystick[nr]);
1812 }
1813
1814 static boolean SDLCheckJoystickOpened(int nr)
1815 {
1816   if (nr < 0 || nr > MAX_PLAYERS)
1817     return FALSE;
1818
1819   return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
1820 }
1821
1822 void HandleJoystickEvent(Event *event)
1823 {
1824   switch(event->type)
1825   {
1826     case SDL_JOYAXISMOTION:
1827       if (event->jaxis.axis < 2)
1828         sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
1829       break;
1830
1831     case SDL_JOYBUTTONDOWN:
1832       if (event->jbutton.button < 2)
1833         sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
1834       break;
1835
1836     case SDL_JOYBUTTONUP:
1837       if (event->jbutton.button < 2)
1838         sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
1839       break;
1840
1841     default:
1842       break;
1843   }
1844 }
1845
1846 void SDLInitJoysticks()
1847 {
1848   static boolean sdl_joystick_subsystem_initialized = FALSE;
1849   boolean print_warning = !sdl_joystick_subsystem_initialized;
1850   int i;
1851
1852   if (!sdl_joystick_subsystem_initialized)
1853   {
1854     sdl_joystick_subsystem_initialized = TRUE;
1855
1856     if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1857     {
1858       Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
1859       return;
1860     }
1861   }
1862
1863   for (i = 0; i < MAX_PLAYERS; i++)
1864   {
1865     /* get configured joystick for this player */
1866     char *device_name = setup.input[i].joy.device_name;
1867     int joystick_nr = getJoystickNrFromDeviceName(device_name);
1868
1869     if (joystick_nr >= SDL_NumJoysticks())
1870     {
1871       if (setup.input[i].use_joystick && print_warning)
1872         Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
1873
1874       joystick_nr = -1;
1875     }
1876
1877     /* misuse joystick file descriptor variable to store joystick number */
1878     joystick.fd[i] = joystick_nr;
1879
1880     if (joystick_nr == -1)
1881       continue;
1882
1883     /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
1884     if (SDLCheckJoystickOpened(joystick_nr))
1885       SDLCloseJoystick(joystick_nr);
1886
1887     if (!setup.input[i].use_joystick)
1888       continue;
1889
1890     if (!SDLOpenJoystick(joystick_nr))
1891     {
1892       if (print_warning)
1893         Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
1894
1895       continue;
1896     }
1897
1898     joystick.status = JOYSTICK_ACTIVATED;
1899   }
1900 }
1901
1902 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
1903 {
1904   if (nr < 0 || nr >= MAX_PLAYERS)
1905     return FALSE;
1906
1907   if (x != NULL)
1908     *x = sdl_js_axis[nr][0];
1909   if (y != NULL)
1910     *y = sdl_js_axis[nr][1];
1911
1912   if (b1 != NULL)
1913     *b1 = sdl_js_button[nr][0];
1914   if (b2 != NULL)
1915     *b2 = sdl_js_button[nr][1];
1916
1917   return TRUE;
1918 }
1919
1920 #endif /* TARGET_SDL */