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