1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
21 /* ========================================================================= */
23 /* ========================================================================= */
25 /* SDL internal variables */
26 #if defined(TARGET_SDL2)
27 static SDL_Window *sdl_window = NULL;
28 static SDL_Renderer *sdl_renderer = NULL;
29 static SDL_Texture *sdl_texture_stream = NULL;
30 static SDL_Texture *sdl_texture_target = NULL;
31 static boolean fullscreen_enabled = FALSE;
34 static boolean limit_screen_updates = FALSE;
37 /* functions from SGE library */
38 void sge_Line(SDL_Surface *, Sint16, Sint16, Sint16, Sint16, Uint32);
40 void SDLLimitScreenUpdates(boolean enable)
42 limit_screen_updates = enable;
45 static void FinalizeScreen(int draw_target)
47 // copy global animations to render target buffer, if defined (below border)
48 if (gfx.draw_global_anim_function != NULL)
49 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_1);
51 // copy global masked border to render target buffer, if defined
52 if (gfx.draw_global_border_function != NULL)
53 gfx.draw_global_border_function(draw_target);
55 // copy global animations to render target buffer, if defined (above border)
56 if (gfx.draw_global_anim_function != NULL)
57 gfx.draw_global_anim_function(draw_target, DRAW_GLOBAL_ANIM_STAGE_2);
60 static void UpdateScreenExt(SDL_Rect *rect, boolean with_frame_delay)
62 static unsigned int update_screen_delay = 0;
63 unsigned int update_screen_delay_value = 50; /* (milliseconds) */
64 SDL_Surface *screen = backbuffer->surface;
66 if (limit_screen_updates &&
67 !DelayReached(&update_screen_delay, update_screen_delay_value))
70 LimitScreenUpdates(FALSE);
74 static int LastFrameCounter = 0;
75 boolean changed = (FrameCounter != LastFrameCounter);
77 printf("::: FrameCounter == %d [%s]\n", FrameCounter,
78 (changed ? "-" : "SAME FRAME UPDATED"));
80 LastFrameCounter = FrameCounter;
89 if (video.screen_rendering_mode == SPECIAL_RENDERING_BITMAP &&
90 gfx.final_screen_bitmap != NULL) // may not be initialized yet
92 // draw global animations using bitmaps instead of using textures
93 // to prevent texture scaling artefacts (this is potentially slower)
95 BlitBitmap(backbuffer, gfx.final_screen_bitmap, 0, 0,
96 gfx.win_xsize, gfx.win_ysize, 0, 0);
98 FinalizeScreen(DRAW_TO_SCREEN);
100 screen = gfx.final_screen_bitmap->surface;
102 // force full window redraw
106 #if defined(TARGET_SDL2)
107 SDL_Texture *sdl_texture = sdl_texture_stream;
109 // deactivate use of target texture if render targets are not supported
110 if ((video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
111 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE) &&
112 sdl_texture_target == NULL)
113 video.screen_rendering_mode = SPECIAL_RENDERING_OFF;
115 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET)
116 sdl_texture = sdl_texture_target;
120 int bytes_x = screen->pitch / video.width;
121 int bytes_y = screen->pitch;
123 SDL_UpdateTexture(sdl_texture, rect,
124 screen->pixels + rect->x * bytes_x + rect->y * bytes_y,
129 SDL_UpdateTexture(sdl_texture, NULL, screen->pixels, screen->pitch);
132 SDL_Rect *src_rect1 = NULL, *dst_rect1 = NULL;
133 SDL_Rect *src_rect2 = NULL, *dst_rect2 = NULL;
134 #if defined(HAS_SCREEN_KEYBOARD)
135 SDL_Rect src_rect_up = { 0, video.height / 2, video.width, video.height / 2 };
136 SDL_Rect dst_rect_up = { 0, 0, video.width, video.height / 2 };
138 if (video.shifted_up)
140 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
141 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
143 src_rect2 = &src_rect_up;
144 dst_rect2 = &dst_rect_up;
148 src_rect1 = &src_rect_up;
149 dst_rect1 = &dst_rect_up;
154 // clear render target buffer
155 SDL_RenderClear(sdl_renderer);
157 // set renderer to use target texture for rendering
158 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
159 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
160 SDL_SetRenderTarget(sdl_renderer, sdl_texture_target);
162 // copy backbuffer texture to render target buffer
163 if (video.screen_rendering_mode != SPECIAL_RENDERING_TARGET)
164 SDL_RenderCopy(sdl_renderer, sdl_texture_stream, src_rect1, dst_rect1);
166 if (video.screen_rendering_mode != SPECIAL_RENDERING_BITMAP)
167 FinalizeScreen(DRAW_TO_SCREEN);
169 // when using target texture, copy it to screen buffer
170 if (video.screen_rendering_mode == SPECIAL_RENDERING_TARGET ||
171 video.screen_rendering_mode == SPECIAL_RENDERING_DOUBLE)
173 SDL_SetRenderTarget(sdl_renderer, NULL);
174 SDL_RenderCopy(sdl_renderer, sdl_texture_target, src_rect2, dst_rect2);
178 // global synchronization point of the game to align video frame delay
179 if (with_frame_delay)
180 WaitUntilDelayReached(&video.frame_delay, video.frame_delay_value);
182 #if defined(TARGET_SDL2)
183 // show render target buffer on screen
184 SDL_RenderPresent(sdl_renderer);
187 SDL_UpdateRects(screen, 1, rect);
189 SDL_UpdateRect(screen, 0, 0, 0, 0);
193 static void UpdateScreen_WithFrameDelay(SDL_Rect *rect)
195 UpdateScreenExt(rect, TRUE);
198 static void UpdateScreen_WithoutFrameDelay(SDL_Rect *rect)
200 UpdateScreenExt(rect, FALSE);
203 static void SDLSetWindowIcon(char *basename)
205 /* (setting the window icon on Mac OS X would replace the high-quality
206 dock icon with the currently smaller (and uglier) icon from file) */
208 #if !defined(PLATFORM_MACOSX)
209 char *filename = getCustomImageFilename(basename);
210 SDL_Surface *surface;
212 if (filename == NULL)
214 Error(ERR_WARN, "SDLSetWindowIcon(): cannot find file '%s'", basename);
219 if ((surface = IMG_Load(filename)) == NULL)
221 Error(ERR_WARN, "IMG_Load() failed: %s", SDL_GetError());
226 /* set transparent color */
227 SDL_SetColorKey(surface, SET_TRANSPARENT_PIXEL,
228 SDL_MapRGB(surface->format, 0x00, 0x00, 0x00));
230 #if defined(TARGET_SDL2)
231 SDL_SetWindowIcon(sdl_window, surface);
233 SDL_WM_SetIcon(surface, NULL);
238 #if defined(TARGET_SDL2)
240 static boolean equalSDLPixelFormat(SDL_PixelFormat *format1,
241 SDL_PixelFormat *format2)
243 return (format1->format == format2->format &&
244 format1->BitsPerPixel == format2->BitsPerPixel &&
245 format1->BytesPerPixel == format2->BytesPerPixel &&
246 format1->Rmask == format2->Rmask &&
247 format1->Gmask == format2->Gmask &&
248 format1->Bmask == format2->Bmask);
251 static Pixel SDLGetColorKey(SDL_Surface *surface)
255 if (SDL_GetColorKey(surface, &color_key) != 0)
261 static boolean SDLHasColorKey(SDL_Surface *surface)
263 return (SDLGetColorKey(surface) != -1);
266 static boolean SDLHasAlpha(SDL_Surface *surface)
268 SDL_BlendMode blend_mode;
270 if (SDL_GetSurfaceBlendMode(surface, &blend_mode) != 0)
273 return (blend_mode == SDL_BLENDMODE_BLEND);
276 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
278 SDL_BlendMode blend_mode = (set ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE);
280 SDL_SetSurfaceBlendMode(surface, blend_mode);
281 SDL_SetSurfaceAlphaMod(surface, alpha);
284 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
286 SDL_PixelFormat format;
287 SDL_Surface *new_surface;
292 if (backbuffer && backbuffer->surface)
294 format = *backbuffer->surface->format;
295 format.Amask = surface->format->Amask; // keep alpha channel
299 format = *surface->format;
302 new_surface = SDL_ConvertSurface(surface, &format, 0);
304 if (new_surface == NULL)
305 Error(ERR_EXIT, "SDL_ConvertSurface() failed: %s", SDL_GetError());
310 boolean SDLSetNativeSurface(SDL_Surface **surface)
312 SDL_Surface *new_surface;
314 if (surface == NULL ||
316 backbuffer == NULL ||
317 backbuffer->surface == NULL)
320 // if pixel format already optimized for destination surface, do nothing
321 if (equalSDLPixelFormat((*surface)->format, backbuffer->surface->format))
324 new_surface = SDLGetNativeSurface(*surface);
326 SDL_FreeSurface(*surface);
328 *surface = new_surface;
335 static Pixel SDLGetColorKey(SDL_Surface *surface)
337 if ((surface->flags & SDL_SRCCOLORKEY) == 0)
340 return surface->format->colorkey;
343 static boolean SDLHasColorKey(SDL_Surface *surface)
345 return (SDLGetColorKey(surface) != -1);
348 static boolean SDLHasAlpha(SDL_Surface *surface)
350 return ((surface->flags & SDL_SRCALPHA) != 0);
353 static void SDLSetAlpha(SDL_Surface *surface, boolean set, int alpha)
355 SDL_SetAlpha(surface, (set ? SDL_SRCALPHA : 0), alpha);
358 SDL_Surface *SDLGetNativeSurface(SDL_Surface *surface)
360 SDL_Surface *new_surface;
365 if (!video.initialized)
366 new_surface = SDL_ConvertSurface(surface, surface->format, SURFACE_FLAGS);
367 else if (SDLHasAlpha(surface))
368 new_surface = SDL_DisplayFormatAlpha(surface);
370 new_surface = SDL_DisplayFormat(surface);
372 if (new_surface == NULL)
373 Error(ERR_EXIT, "%s() failed: %s",
374 (video.initialized ? "SDL_DisplayFormat" : "SDL_ConvertSurface"),
380 boolean SDLSetNativeSurface(SDL_Surface **surface)
382 SDL_Surface *new_surface;
384 if (surface == NULL ||
389 new_surface = SDLGetNativeSurface(*surface);
391 SDL_FreeSurface(*surface);
393 *surface = new_surface;
400 #if defined(TARGET_SDL2)
401 static SDL_Texture *SDLCreateTextureFromSurface(SDL_Surface *surface)
403 SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_renderer, surface);
406 Error(ERR_EXIT, "SDL_CreateTextureFromSurface() failed: %s",
413 void SDLCreateBitmapTextures(Bitmap *bitmap)
415 #if defined(TARGET_SDL2)
420 SDL_DestroyTexture(bitmap->texture);
421 if (bitmap->texture_masked)
422 SDL_DestroyTexture(bitmap->texture_masked);
424 bitmap->texture = SDLCreateTextureFromSurface(bitmap->surface);
425 bitmap->texture_masked = SDLCreateTextureFromSurface(bitmap->surface_masked);
429 void SDLFreeBitmapTextures(Bitmap *bitmap)
431 #if defined(TARGET_SDL2)
436 SDL_DestroyTexture(bitmap->texture);
437 if (bitmap->texture_masked)
438 SDL_DestroyTexture(bitmap->texture_masked);
440 bitmap->texture = NULL;
441 bitmap->texture_masked = NULL;
445 void SDLInitVideoDisplay(void)
447 #if !defined(TARGET_SDL2)
448 if (!strEqual(setup.system.sdl_videodriver, ARG_DEFAULT))
449 SDL_putenv(getStringCat2("SDL_VIDEODRIVER=", setup.system.sdl_videodriver));
451 SDL_putenv("SDL_VIDEO_CENTERED=1");
454 /* initialize SDL video */
455 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
456 Error(ERR_EXIT, "SDL_InitSubSystem() failed: %s", SDL_GetError());
458 /* set default SDL depth */
459 #if !defined(TARGET_SDL2)
460 video.default_depth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
462 video.default_depth = 32; // (how to determine video depth in SDL2?)
466 void SDLInitVideoBuffer(boolean fullscreen)
468 video.window_scaling_percent = setup.window_scaling_percent;
469 video.window_scaling_quality = setup.window_scaling_quality;
471 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
473 #if defined(TARGET_SDL2)
474 // SDL 2.0: support for (desktop) fullscreen mode available
475 video.fullscreen_available = TRUE;
477 // SDL 1.2: no support for fullscreen mode in R'n'D anymore
478 video.fullscreen_available = FALSE;
481 /* open SDL video output device (window or fullscreen mode) */
482 if (!SDLSetVideoMode(fullscreen))
483 Error(ERR_EXIT, "setting video mode failed");
485 /* !!! SDL2 can only set the window icon if the window already exists !!! */
486 /* set window icon */
487 SDLSetWindowIcon(program.icon_filename);
489 /* set window and icon title */
490 #if defined(TARGET_SDL2)
491 SDL_SetWindowTitle(sdl_window, program.window_title);
493 SDL_WM_SetCaption(program.window_title, program.window_title);
496 /* SDL cannot directly draw to the visible video framebuffer like X11,
497 but always uses a backbuffer, which is then blitted to the visible
498 video framebuffer with 'SDL_UpdateRect' (or replaced with the current
499 visible video framebuffer with 'SDL_Flip', if the hardware supports
500 this). Therefore do not use an additional backbuffer for drawing, but
501 use a symbolic buffer (distinguishable from the SDL backbuffer) called
502 'window', which indicates that the SDL backbuffer should be updated to
503 the visible video framebuffer when attempting to blit to it.
505 For convenience, it seems to be a good idea to create this symbolic
506 buffer 'window' at the same size as the SDL backbuffer. Although it
507 should never be drawn to directly, it would do no harm nevertheless. */
509 /* create additional (symbolic) buffer for double-buffering */
510 ReCreateBitmap(&window, video.width, video.height);
513 static boolean SDLCreateScreen(boolean fullscreen)
515 SDL_Surface *new_surface = NULL;
517 #if defined(TARGET_SDL2)
518 int surface_flags_window = SURFACE_FLAGS | SDL_WINDOW_RESIZABLE;
519 int surface_flags_fullscreen = SURFACE_FLAGS | SDL_WINDOW_FULLSCREEN_DESKTOP;
521 int surface_flags_window = SURFACE_FLAGS;
522 int surface_flags_fullscreen = SURFACE_FLAGS; // (no fullscreen in SDL 1.2)
525 #if defined(TARGET_SDL2)
527 int renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
529 /* If SDL_CreateRenderer() is called from within a VirtualBox Windows VM
530 _without_ enabling 2D/3D acceleration and/or guest additions installed,
531 it will crash if flags are *not* set to SDL_RENDERER_SOFTWARE (because
532 it will try to use accelerated graphics and apparently fails miserably) */
533 int renderer_flags = SDL_RENDERER_SOFTWARE;
537 int width = video.width;
538 int height = video.height;
539 int surface_flags = (fullscreen ? surface_flags_fullscreen :
540 surface_flags_window);
542 // default window size is unscaled
543 video.window_width = video.width;
544 video.window_height = video.height;
546 #if defined(TARGET_SDL2)
548 // store if initial screen mode is fullscreen mode when changing screen size
549 video.fullscreen_initial = fullscreen;
551 float window_scaling_factor = (float)setup.window_scaling_percent / 100;
553 video.window_width = window_scaling_factor * width;
554 video.window_height = window_scaling_factor * height;
556 if (sdl_texture_stream)
558 SDL_DestroyTexture(sdl_texture_stream);
559 sdl_texture_stream = NULL;
562 if (sdl_texture_target)
564 SDL_DestroyTexture(sdl_texture_target);
565 sdl_texture_target = NULL;
568 if (!(fullscreen && fullscreen_enabled))
572 SDL_DestroyRenderer(sdl_renderer);
578 SDL_DestroyWindow(sdl_window);
583 if (sdl_window == NULL)
584 sdl_window = SDL_CreateWindow(program.window_title,
585 SDL_WINDOWPOS_CENTERED,
586 SDL_WINDOWPOS_CENTERED,
591 if (sdl_window != NULL)
593 if (sdl_renderer == NULL)
594 sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
596 if (sdl_renderer != NULL)
598 SDL_RenderSetLogicalSize(sdl_renderer, width, height);
599 // SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
600 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, setup.window_scaling_quality);
602 sdl_texture_stream = SDL_CreateTexture(sdl_renderer,
603 SDL_PIXELFORMAT_ARGB8888,
604 SDL_TEXTUREACCESS_STREAMING,
607 if (SDL_RenderTargetSupported(sdl_renderer))
608 sdl_texture_target = SDL_CreateTexture(sdl_renderer,
609 SDL_PIXELFORMAT_ARGB8888,
610 SDL_TEXTUREACCESS_TARGET,
613 if (sdl_texture_stream != NULL)
615 // use SDL default values for RGB masks and no alpha channel
616 new_surface = SDL_CreateRGBSurface(0, width, height, 32, 0,0,0, 0);
618 if (new_surface == NULL)
619 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
623 Error(ERR_WARN, "SDL_CreateTexture() failed: %s", SDL_GetError());
628 Error(ERR_WARN, "SDL_CreateRenderer() failed: %s", SDL_GetError());
633 Error(ERR_WARN, "SDL_CreateWindow() failed: %s", SDL_GetError());
638 if (gfx.final_screen_bitmap == NULL)
639 gfx.final_screen_bitmap = CreateBitmapStruct();
641 gfx.final_screen_bitmap->width = width;
642 gfx.final_screen_bitmap->height = height;
644 gfx.final_screen_bitmap->surface =
645 SDL_SetVideoMode(width, height, video.depth, surface_flags);
647 if (gfx.final_screen_bitmap->surface != NULL)
650 SDL_CreateRGBSurface(surface_flags, width, height, video.depth, 0,0,0, 0);
652 if (new_surface == NULL)
653 Error(ERR_WARN, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
656 new_surface = gfx.final_screen_bitmap->surface;
657 gfx.final_screen_bitmap = NULL;
663 Error(ERR_WARN, "SDL_SetVideoMode() failed: %s", SDL_GetError());
667 #if defined(TARGET_SDL2)
668 // store fullscreen state ("video.fullscreen_enabled" may not reflect this!)
669 if (new_surface != NULL)
670 fullscreen_enabled = fullscreen;
673 if (backbuffer == NULL)
674 backbuffer = CreateBitmapStruct();
676 backbuffer->width = video.width;
677 backbuffer->height = video.height;
679 if (backbuffer->surface)
680 SDL_FreeSurface(backbuffer->surface);
682 backbuffer->surface = new_surface;
684 return (new_surface != NULL);
687 boolean SDLSetVideoMode(boolean fullscreen)
689 boolean success = FALSE;
693 if (fullscreen && !video.fullscreen_enabled && video.fullscreen_available)
695 /* switch display to fullscreen mode, if available */
696 success = SDLCreateScreen(TRUE);
700 /* switching display to fullscreen mode failed -- do not try it again */
701 video.fullscreen_available = FALSE;
705 video.fullscreen_enabled = TRUE;
709 if ((!fullscreen && video.fullscreen_enabled) || !success)
711 /* switch display to window mode */
712 success = SDLCreateScreen(FALSE);
716 /* switching display to window mode failed -- should not happen */
720 video.fullscreen_enabled = FALSE;
721 video.window_scaling_percent = setup.window_scaling_percent;
722 video.window_scaling_quality = setup.window_scaling_quality;
724 SDLSetScreenRenderingMode(setup.screen_rendering_mode);
728 #if defined(TARGET_SDL2)
729 SDLRedrawWindow(); // map window
733 #if defined(PLATFORM_WIN32)
734 // experimental drag and drop code
736 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
739 SDL_SysWMinfo wminfo;
741 boolean wminfo_success = FALSE;
743 SDL_VERSION(&wminfo.version);
744 #if defined(TARGET_SDL2)
746 wminfo_success = SDL_GetWindowWMInfo(sdl_window, &wminfo);
748 wminfo_success = (SDL_GetWMInfo(&wminfo) == 1);
753 #if defined(TARGET_SDL2)
754 hwnd = wminfo.info.win.window;
756 hwnd = wminfo.window;
759 DragAcceptFiles(hwnd, TRUE);
768 void SDLSetWindowTitle()
770 #if defined(TARGET_SDL2)
771 SDL_SetWindowTitle(sdl_window, program.window_title);
773 SDL_WM_SetCaption(program.window_title, program.window_title);
777 #if defined(TARGET_SDL2)
778 void SDLSetWindowScaling(int window_scaling_percent)
780 if (sdl_window == NULL)
783 float window_scaling_factor = (float)window_scaling_percent / 100;
784 int new_window_width = (int)(window_scaling_factor * video.width);
785 int new_window_height = (int)(window_scaling_factor * video.height);
787 SDL_SetWindowSize(sdl_window, new_window_width, new_window_height);
789 video.window_scaling_percent = window_scaling_percent;
790 video.window_width = new_window_width;
791 video.window_height = new_window_height;
796 void SDLSetWindowScalingQuality(char *window_scaling_quality)
798 SDL_Texture *new_texture;
800 if (sdl_texture_stream == NULL)
803 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, window_scaling_quality);
805 new_texture = SDL_CreateTexture(sdl_renderer,
806 SDL_PIXELFORMAT_ARGB8888,
807 SDL_TEXTUREACCESS_STREAMING,
808 video.width, video.height);
810 if (new_texture != NULL)
812 SDL_DestroyTexture(sdl_texture_stream);
814 sdl_texture_stream = new_texture;
817 if (SDL_RenderTargetSupported(sdl_renderer))
818 new_texture = SDL_CreateTexture(sdl_renderer,
819 SDL_PIXELFORMAT_ARGB8888,
820 SDL_TEXTUREACCESS_TARGET,
821 video.width, video.height);
825 if (new_texture != NULL)
827 SDL_DestroyTexture(sdl_texture_target);
829 sdl_texture_target = new_texture;
834 video.window_scaling_quality = window_scaling_quality;
837 void SDLSetWindowFullscreen(boolean fullscreen)
839 if (sdl_window == NULL)
842 int flags = (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
844 if (SDL_SetWindowFullscreen(sdl_window, flags) == 0)
845 video.fullscreen_enabled = fullscreen_enabled = fullscreen;
847 // if screen size was changed in fullscreen mode, correct desktop window size
848 if (!fullscreen && video.fullscreen_initial)
850 SDLSetWindowScaling(setup.window_scaling_percent);
851 SDL_SetWindowPosition(sdl_window,
852 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
854 video.fullscreen_initial = FALSE;
859 void SDLSetScreenRenderingMode(char *screen_rendering_mode)
861 #if defined(TARGET_SDL2)
862 video.screen_rendering_mode =
863 (strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_BITMAP) ?
864 SPECIAL_RENDERING_BITMAP :
865 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_TARGET) ?
866 SPECIAL_RENDERING_TARGET:
867 strEqual(screen_rendering_mode, STR_SPECIAL_RENDERING_DOUBLE) ?
868 SPECIAL_RENDERING_DOUBLE : SPECIAL_RENDERING_OFF);
870 video.screen_rendering_mode = SPECIAL_RENDERING_BITMAP;
874 void SDLRedrawWindow()
876 UpdateScreen_WithoutFrameDelay(NULL);
879 void SDLCreateBitmapContent(Bitmap *bitmap, int width, int height,
882 SDL_Surface *surface =
883 SDL_CreateRGBSurface(SURFACE_FLAGS, width, height, depth, 0,0,0, 0);
886 Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
888 SDLSetNativeSurface(&surface);
890 bitmap->surface = surface;
893 void SDLFreeBitmapPointers(Bitmap *bitmap)
896 SDL_FreeSurface(bitmap->surface);
897 if (bitmap->surface_masked)
898 SDL_FreeSurface(bitmap->surface_masked);
900 bitmap->surface = NULL;
901 bitmap->surface_masked = NULL;
903 #if defined(TARGET_SDL2)
905 SDL_DestroyTexture(bitmap->texture);
906 if (bitmap->texture_masked)
907 SDL_DestroyTexture(bitmap->texture_masked);
909 bitmap->texture = NULL;
910 bitmap->texture_masked = NULL;
914 void SDLCopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
915 int src_x, int src_y, int width, int height,
916 int dst_x, int dst_y, int mask_mode)
918 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
919 SDL_Rect src_rect, dst_rect;
931 // if (src_bitmap != backbuffer || dst_bitmap != window)
932 if (!(src_bitmap == backbuffer && dst_bitmap == window))
933 SDL_BlitSurface((mask_mode == BLIT_MASKED ?
934 src_bitmap->surface_masked : src_bitmap->surface),
935 &src_rect, real_dst_bitmap->surface, &dst_rect);
937 if (dst_bitmap == window)
938 UpdateScreen_WithFrameDelay(&dst_rect);
941 void SDLBlitTexture(Bitmap *bitmap,
942 int src_x, int src_y, int width, int height,
943 int dst_x, int dst_y, int mask_mode)
945 #if defined(TARGET_SDL2)
946 SDL_Texture *texture;
951 (mask_mode == BLIT_MASKED ? bitmap->texture_masked : bitmap->texture);
966 SDL_RenderCopy(sdl_renderer, texture, &src_rect, &dst_rect);
970 void SDLFillRectangle(Bitmap *dst_bitmap, int x, int y, int width, int height,
973 Bitmap *real_dst_bitmap = (dst_bitmap == window ? backbuffer : dst_bitmap);
981 SDL_FillRect(real_dst_bitmap->surface, &rect, color);
983 if (dst_bitmap == window)
984 UpdateScreen_WithFrameDelay(&rect);
987 void PrepareFadeBitmap(int draw_target)
989 Bitmap *fade_bitmap =
990 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
991 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
993 if (fade_bitmap == NULL)
996 // copy backbuffer to fading buffer
997 BlitBitmap(backbuffer, fade_bitmap, 0, 0, gfx.win_xsize, gfx.win_ysize, 0, 0);
999 // add border and animations to fading buffer
1000 FinalizeScreen(draw_target);
1003 void SDLFadeRectangle(int x, int y, int width, int height,
1004 int fade_mode, int fade_delay, int post_delay,
1005 void (*draw_border_function)(void))
1007 SDL_Surface *surface_backup = gfx.fade_bitmap_backup->surface;
1008 SDL_Surface *surface_source = gfx.fade_bitmap_source->surface;
1009 SDL_Surface *surface_target = gfx.fade_bitmap_target->surface;
1010 SDL_Surface *surface_black = gfx.fade_bitmap_black->surface;
1011 SDL_Surface *surface_screen = backbuffer->surface;
1012 SDL_Rect src_rect, dst_rect;
1014 int src_x = x, src_y = y;
1015 int dst_x = x, dst_y = y;
1016 unsigned int time_last, time_current;
1018 // store function for drawing global masked border
1019 void (*draw_global_border_function)(int) = gfx.draw_global_border_function;
1021 // deactivate drawing of global border while fading, if needed
1022 if (draw_border_function == NULL)
1023 gfx.draw_global_border_function = NULL;
1028 src_rect.h = height;
1032 dst_rect.w = width; /* (ignored) */
1033 dst_rect.h = height; /* (ignored) */
1035 dst_rect2 = dst_rect;
1037 // before fading in, store backbuffer (without animation graphics)
1038 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1039 SDL_BlitSurface(surface_screen, &dst_rect, surface_backup, &src_rect);
1041 /* copy source and target surfaces to temporary surfaces for fading */
1042 if (fade_mode & FADE_TYPE_TRANSFORM)
1044 // (source and target fading buffer already prepared)
1046 else if (fade_mode & FADE_TYPE_FADE_IN)
1048 // (target fading buffer already prepared)
1049 SDL_BlitSurface(surface_black, &src_rect, surface_source, &src_rect);
1051 else /* FADE_TYPE_FADE_OUT */
1053 // (source fading buffer already prepared)
1054 SDL_BlitSurface(surface_black, &src_rect, surface_target, &src_rect);
1057 time_current = SDL_GetTicks();
1059 if (fade_mode == FADE_MODE_MELT)
1061 boolean done = FALSE;
1062 int melt_pixels = 2;
1063 int melt_columns = width / melt_pixels;
1064 int ypos[melt_columns];
1065 int max_steps = height / 8 + 32;
1070 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1072 SDLSetAlpha(surface_target, FALSE, 0); /* disable alpha blending */
1074 ypos[0] = -GetSimpleRandom(16);
1076 for (i = 1 ; i < melt_columns; i++)
1078 int r = GetSimpleRandom(3) - 1; /* randomly choose from { -1, 0, -1 } */
1080 ypos[i] = ypos[i - 1] + r;
1093 time_last = time_current;
1094 time_current = SDL_GetTicks();
1095 steps += max_steps * ((float)(time_current - time_last) / fade_delay);
1096 steps_final = MIN(MAX(0, steps), max_steps);
1100 done = (steps_done >= steps_final);
1102 for (i = 0 ; i < melt_columns; i++)
1110 else if (ypos[i] < height)
1115 int dy = (ypos[i] < y1) ? ypos[i] + 1 : y2 + GetSimpleRandom(y3);
1117 if (ypos[i] + dy >= height)
1118 dy = height - ypos[i];
1120 /* copy part of (appearing) target surface to upper area */
1121 src_rect.x = src_x + i * melt_pixels;
1122 // src_rect.y = src_y + ypos[i];
1124 src_rect.w = melt_pixels;
1126 src_rect.h = ypos[i] + dy;
1128 dst_rect.x = dst_x + i * melt_pixels;
1129 // dst_rect.y = dst_y + ypos[i];
1132 if (steps_done >= steps_final)
1133 SDL_BlitSurface(surface_target, &src_rect,
1134 surface_screen, &dst_rect);
1138 /* copy part of (disappearing) source surface to lower area */
1139 src_rect.x = src_x + i * melt_pixels;
1141 src_rect.w = melt_pixels;
1142 src_rect.h = height - ypos[i];
1144 dst_rect.x = dst_x + i * melt_pixels;
1145 dst_rect.y = dst_y + ypos[i];
1147 if (steps_done >= steps_final)
1148 SDL_BlitSurface(surface_source, &src_rect,
1149 surface_screen, &dst_rect);
1155 src_rect.x = src_x + i * melt_pixels;
1157 src_rect.w = melt_pixels;
1158 src_rect.h = height;
1160 dst_rect.x = dst_x + i * melt_pixels;
1163 if (steps_done >= steps_final)
1164 SDL_BlitSurface(surface_target, &src_rect,
1165 surface_screen, &dst_rect);
1169 if (steps_done >= steps_final)
1171 if (draw_border_function != NULL)
1172 draw_border_function();
1174 UpdateScreen_WithFrameDelay(&dst_rect2);
1178 else if (fade_mode == FADE_MODE_CURTAIN)
1182 int xx_size = width / 2;
1184 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1186 SDLSetAlpha(surface_source, FALSE, 0); /* disable alpha blending */
1188 for (xx = 0; xx < xx_size;)
1190 time_last = time_current;
1191 time_current = SDL_GetTicks();
1192 xx += xx_size * ((float)(time_current - time_last) / fade_delay);
1193 xx_final = MIN(MAX(0, xx), xx_size);
1198 src_rect.h = height;
1203 /* draw new (target) image to screen buffer */
1204 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1206 if (xx_final < xx_size)
1208 src_rect.w = xx_size - xx_final;
1209 src_rect.h = height;
1211 /* draw old (source) image to screen buffer (left side) */
1213 src_rect.x = src_x + xx_final;
1216 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1218 /* draw old (source) image to screen buffer (right side) */
1220 src_rect.x = src_x + xx_size;
1221 dst_rect.x = dst_x + xx_size + xx_final;
1223 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1226 if (draw_border_function != NULL)
1227 draw_border_function();
1229 /* only update the region of the screen that is affected from fading */
1230 UpdateScreen_WithFrameDelay(&dst_rect2);
1233 else /* fading in, fading out or cross-fading */
1238 for (alpha = 0.0; alpha < 255.0;)
1240 time_last = time_current;
1241 time_current = SDL_GetTicks();
1242 alpha += 255 * ((float)(time_current - time_last) / fade_delay);
1243 alpha_final = MIN(MAX(0, alpha), 255);
1245 /* draw existing (source) image to screen buffer */
1246 SDL_BlitSurface(surface_source, &src_rect, surface_screen, &dst_rect);
1248 /* draw new (target) image to screen buffer using alpha blending */
1249 SDLSetAlpha(surface_target, TRUE, alpha_final);
1250 SDL_BlitSurface(surface_target, &src_rect, surface_screen, &dst_rect);
1252 if (draw_border_function != NULL)
1253 draw_border_function();
1255 /* only update the region of the screen that is affected from fading */
1256 UpdateScreen_WithFrameDelay(&dst_rect);
1262 unsigned int time_post_delay;
1264 time_current = SDL_GetTicks();
1265 time_post_delay = time_current + post_delay;
1267 while (time_current < time_post_delay)
1269 // updating the screen contains waiting for frame delay (non-busy)
1270 UpdateScreen_WithFrameDelay(NULL);
1272 time_current = SDL_GetTicks();
1276 // restore function for drawing global masked border
1277 gfx.draw_global_border_function = draw_global_border_function;
1279 // after fading in, restore backbuffer (without animation graphics)
1280 if (fade_mode & (FADE_TYPE_FADE_IN | FADE_TYPE_TRANSFORM))
1281 SDL_BlitSurface(surface_backup, &dst_rect, surface_screen, &src_rect);
1284 void SDLDrawSimpleLine(Bitmap *dst_bitmap, int from_x, int from_y,
1285 int to_x, int to_y, Uint32 color)
1287 SDL_Surface *surface = dst_bitmap->surface;
1291 swap_numbers(&from_x, &to_x);
1294 swap_numbers(&from_y, &to_y);
1298 rect.w = (to_x - from_x + 1);
1299 rect.h = (to_y - from_y + 1);
1301 SDL_FillRect(surface, &rect, color);
1304 void SDLDrawLine(Bitmap *dst_bitmap, int from_x, int from_y,
1305 int to_x, int to_y, Uint32 color)
1307 sge_Line(dst_bitmap->surface, from_x, from_y, to_x, to_y, color);
1310 #if ENABLE_UNUSED_CODE
1311 void SDLDrawLines(SDL_Surface *surface, struct XY *points,
1312 int num_points, Uint32 color)
1317 for (i = 0; i < num_points - 1; i++)
1319 for (x = 0; x < line_width; x++)
1321 for (y = 0; y < line_width; y++)
1323 int dx = x - line_width / 2;
1324 int dy = y - line_width / 2;
1326 if ((x == 0 && y == 0) ||
1327 (x == 0 && y == line_width - 1) ||
1328 (x == line_width - 1 && y == 0) ||
1329 (x == line_width - 1 && y == line_width - 1))
1332 sge_Line(surface, points[i].x + dx, points[i].y + dy,
1333 points[i+1].x + dx, points[i+1].y + dy, color);
1340 Pixel SDLGetPixel(Bitmap *src_bitmap, int x, int y)
1342 SDL_Surface *surface = src_bitmap->surface;
1344 switch (surface->format->BytesPerPixel)
1346 case 1: /* assuming 8-bpp */
1348 return *((Uint8 *)surface->pixels + y * surface->pitch + x);
1352 case 2: /* probably 15-bpp or 16-bpp */
1354 return *((Uint16 *)surface->pixels + y * surface->pitch / 2 + x);
1358 case 3: /* slow 24-bpp mode; usually not used */
1360 /* does this work? */
1361 Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
1365 shift = surface->format->Rshift;
1366 color |= *(pix + shift / 8) >> shift;
1367 shift = surface->format->Gshift;
1368 color |= *(pix + shift / 8) >> shift;
1369 shift = surface->format->Bshift;
1370 color |= *(pix + shift / 8) >> shift;
1376 case 4: /* probably 32-bpp */
1378 return *((Uint32 *)surface->pixels + y * surface->pitch / 4 + x);
1387 /* ========================================================================= */
1388 /* The following functions were taken from the SGE library */
1389 /* (SDL Graphics Extension Library) by Anders Lindström */
1390 /* http://www.etek.chalmers.se/~e8cal1/sge/index.html */
1391 /* ========================================================================= */
1393 void _PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1395 if (x >= 0 && x <= surface->w - 1 && y >= 0 && y <= surface->h - 1)
1397 switch (surface->format->BytesPerPixel)
1401 /* Assuming 8-bpp */
1402 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1408 /* Probably 15-bpp or 16-bpp */
1409 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1415 /* Slow 24-bpp mode, usually not used */
1419 /* Gack - slow, but endian correct */
1420 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1421 shift = surface->format->Rshift;
1422 *(pix+shift/8) = color>>shift;
1423 shift = surface->format->Gshift;
1424 *(pix+shift/8) = color>>shift;
1425 shift = surface->format->Bshift;
1426 *(pix+shift/8) = color>>shift;
1432 /* Probably 32-bpp */
1433 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1440 void _PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1441 Uint8 R, Uint8 G, Uint8 B)
1443 _PutPixel(surface, x, y, SDL_MapRGB(surface->format, R, G, B));
1446 void _PutPixel8(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1448 *((Uint8 *)surface->pixels + y*surface->pitch + x) = color;
1451 void _PutPixel16(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1453 *((Uint16 *)surface->pixels + y*surface->pitch/2 + x) = color;
1456 void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1461 /* Gack - slow, but endian correct */
1462 pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3;
1463 shift = surface->format->Rshift;
1464 *(pix+shift/8) = color>>shift;
1465 shift = surface->format->Gshift;
1466 *(pix+shift/8) = color>>shift;
1467 shift = surface->format->Bshift;
1468 *(pix+shift/8) = color>>shift;
1471 void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1473 *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color;
1476 void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color)
1478 switch (dest->format->BytesPerPixel)
1481 *((Uint8 *)dest->pixels + y*dest->pitch + x) = color;
1485 *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color;
1489 _PutPixel24(dest,x,y,color);
1493 *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color;
1498 void sge_PutPixel(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color)
1500 if (SDL_MUSTLOCK(surface))
1502 if (SDL_LockSurface(surface) < 0)
1508 _PutPixel(surface, x, y, color);
1510 if (SDL_MUSTLOCK(surface))
1512 SDL_UnlockSurface(surface);
1516 void sge_PutPixelRGB(SDL_Surface *surface, Sint16 x, Sint16 y,
1517 Uint8 r, Uint8 g, Uint8 b)
1519 sge_PutPixel(surface, x, y, SDL_MapRGB(surface->format, r, g, b));
1522 Sint32 sge_CalcYPitch(SDL_Surface *dest, Sint16 y)
1524 if (y >= 0 && y <= dest->h - 1)
1526 switch (dest->format->BytesPerPixel)
1529 return y*dest->pitch;
1533 return y*dest->pitch/2;
1537 return y*dest->pitch;
1541 return y*dest->pitch/4;
1549 void sge_pPutPixel(SDL_Surface *surface, Sint16 x, Sint32 ypitch, Uint32 color)
1551 if (x >= 0 && x <= surface->w - 1 && ypitch >= 0)
1553 switch (surface->format->BytesPerPixel)
1557 /* Assuming 8-bpp */
1558 *((Uint8 *)surface->pixels + ypitch + x) = color;
1564 /* Probably 15-bpp or 16-bpp */
1565 *((Uint16 *)surface->pixels + ypitch + x) = color;
1571 /* Slow 24-bpp mode, usually not used */
1575 /* Gack - slow, but endian correct */
1576 pix = (Uint8 *)surface->pixels + ypitch + x*3;
1577 shift = surface->format->Rshift;
1578 *(pix+shift/8) = color>>shift;
1579 shift = surface->format->Gshift;
1580 *(pix+shift/8) = color>>shift;
1581 shift = surface->format->Bshift;
1582 *(pix+shift/8) = color>>shift;
1588 /* Probably 32-bpp */
1589 *((Uint32 *)surface->pixels + ypitch + x) = color;
1596 void sge_HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1601 if (SDL_MUSTLOCK(Surface))
1603 if (SDL_LockSurface(Surface) < 0)
1616 /* Do the clipping */
1617 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1621 if (x2 > Surface->w - 1)
1622 x2 = Surface->w - 1;
1629 SDL_FillRect(Surface, &l, Color);
1631 if (SDL_MUSTLOCK(Surface))
1633 SDL_UnlockSurface(Surface);
1637 void sge_HLineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y,
1638 Uint8 R, Uint8 G, Uint8 B)
1640 sge_HLine(Surface, x1, x2, y, SDL_MapRGB(Surface->format, R, G, B));
1643 void _HLine(SDL_Surface *Surface, Sint16 x1, Sint16 x2, Sint16 y, Uint32 Color)
1654 /* Do the clipping */
1655 if (y < 0 || y > Surface->h - 1 || x1 > Surface->w - 1 || x2 < 0)
1659 if (x2 > Surface->w - 1)
1660 x2 = Surface->w - 1;
1667 SDL_FillRect(Surface, &l, Color);
1670 void sge_VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1675 if (SDL_MUSTLOCK(Surface))
1677 if (SDL_LockSurface(Surface) < 0)
1690 /* Do the clipping */
1691 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1695 if (y2 > Surface->h - 1)
1696 y2 = Surface->h - 1;
1703 SDL_FillRect(Surface, &l, Color);
1705 if (SDL_MUSTLOCK(Surface))
1707 SDL_UnlockSurface(Surface);
1711 void sge_VLineRGB(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2,
1712 Uint8 R, Uint8 G, Uint8 B)
1714 sge_VLine(Surface, x, y1, y2, SDL_MapRGB(Surface->format, R, G, B));
1717 void _VLine(SDL_Surface *Surface, Sint16 x, Sint16 y1, Sint16 y2, Uint32 Color)
1728 /* Do the clipping */
1729 if (x < 0 || x > Surface->w - 1 || y1 > Surface->h - 1 || y2 < 0)
1733 if (y2 > Surface->h - 1)
1734 y2 = Surface->h - 1;
1741 SDL_FillRect(Surface, &l, Color);
1744 void sge_DoLine(SDL_Surface *Surface, Sint16 x1, Sint16 y1,
1745 Sint16 x2, Sint16 y2, Uint32 Color,
1746 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1749 Sint16 dx, dy, sdx, sdy, x, y, px, py;
1754 sdx = (dx < 0) ? -1 : 1;
1755 sdy = (dy < 0) ? -1 : 1;
1767 for (x = 0; x < dx; x++)
1769 Callback(Surface, px, py, Color);
1783 for (y = 0; y < dy; y++)
1785 Callback(Surface, px, py, Color);
1799 void sge_DoLineRGB(SDL_Surface *Surface, Sint16 X1, Sint16 Y1,
1800 Sint16 X2, Sint16 Y2, Uint8 R, Uint8 G, Uint8 B,
1801 void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y,
1804 sge_DoLine(Surface, X1, Y1, X2, Y2,
1805 SDL_MapRGB(Surface->format, R, G, B), Callback);
1808 void sge_Line(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2,
1811 if (SDL_MUSTLOCK(Surface))
1813 if (SDL_LockSurface(Surface) < 0)
1818 sge_DoLine(Surface, x1, y1, x2, y2, Color, _PutPixel);
1820 /* unlock the display */
1821 if (SDL_MUSTLOCK(Surface))
1823 SDL_UnlockSurface(Surface);
1827 void sge_LineRGB(SDL_Surface *Surface, Sint16 x1, Sint16 y1, Sint16 x2,
1828 Sint16 y2, Uint8 R, Uint8 G, Uint8 B)
1830 sge_Line(Surface, x1, y1, x2, y2, SDL_MapRGB(Surface->format, R, G, B));
1833 void SDLPutPixel(Bitmap *dst_bitmap, int x, int y, Pixel pixel)
1835 sge_PutPixel(dst_bitmap->surface, x, y, pixel);
1840 -----------------------------------------------------------------------------
1841 quick (no, it's slow) and dirty hack to "invert" rectangle inside SDL surface
1842 -----------------------------------------------------------------------------
1845 void SDLInvertArea(Bitmap *bitmap, int src_x, int src_y,
1846 int width, int height, Uint32 color)
1850 for (y = src_y; y < src_y + height; y++)
1852 for (x = src_x; x < src_x + width; x++)
1854 Uint32 pixel = SDLGetPixel(bitmap, x, y);
1856 SDLPutPixel(bitmap, x, y, pixel == BLACK_PIXEL ? color : BLACK_PIXEL);
1861 void SDLCopyInverseMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
1862 int src_x, int src_y, int width, int height,
1863 int dst_x, int dst_y)
1867 for (y = 0; y < height; y++)
1869 for (x = 0; x < width; x++)
1871 Uint32 pixel = SDLGetPixel(src_bitmap, src_x + x, src_y + y);
1873 if (pixel != BLACK_PIXEL)
1874 SDLPutPixel(dst_bitmap, dst_x + x, dst_y + y, BLACK_PIXEL);
1880 /* ========================================================================= */
1881 /* The following functions were taken from the SDL_gfx library version 2.0.3 */
1882 /* (Rotozoomer) by Andreas Schiffler */
1883 /* http://www.ferzkopp.net/Software/SDL_gfx-2.0/index.html */
1884 /* ========================================================================= */
1887 -----------------------------------------------------------------------------
1890 zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
1891 -----------------------------------------------------------------------------
1902 int zoomSurfaceRGBA_scaleDownBy2(SDL_Surface *src, SDL_Surface *dst)
1905 tColorRGBA *sp, *csp, *dp;
1909 sp = csp = (tColorRGBA *) src->pixels;
1910 dp = (tColorRGBA *) dst->pixels;
1911 dgap = dst->pitch - dst->w * 4;
1913 for (y = 0; y < dst->h; y++)
1917 for (x = 0; x < dst->w; x++)
1919 tColorRGBA *sp0 = sp;
1920 tColorRGBA *sp1 = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
1921 tColorRGBA *sp00 = &sp0[0];
1922 tColorRGBA *sp01 = &sp0[1];
1923 tColorRGBA *sp10 = &sp1[0];
1924 tColorRGBA *sp11 = &sp1[1];
1927 /* create new color pixel from all four source color pixels */
1928 new.r = (sp00->r + sp01->r + sp10->r + sp11->r) / 4;
1929 new.g = (sp00->g + sp01->g + sp10->g + sp11->g) / 4;
1930 new.b = (sp00->b + sp01->b + sp10->b + sp11->b) / 4;
1931 new.a = (sp00->a + sp01->a + sp10->a + sp11->a) / 4;
1936 /* advance source pointers */
1939 /* advance destination pointer */
1943 /* advance source pointer */
1944 csp = (tColorRGBA *) ((Uint8 *) csp + 2 * src->pitch);
1946 /* advance destination pointers */
1947 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
1953 int zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst)
1955 int x, y, *sax, *say, *csax, *csay;
1957 tColorRGBA *sp, *csp, *csp0, *dp;
1960 /* use specialized zoom function when scaling down to exactly half size */
1961 if (src->w == 2 * dst->w &&
1962 src->h == 2 * dst->h)
1963 return zoomSurfaceRGBA_scaleDownBy2(src, dst);
1965 /* variable setup */
1966 sx = (float) src->w / (float) dst->w;
1967 sy = (float) src->h / (float) dst->h;
1969 /* allocate memory for row increments */
1970 csax = sax = (int *)checked_malloc((dst->w + 1) * sizeof(Uint32));
1971 csay = say = (int *)checked_malloc((dst->h + 1) * sizeof(Uint32));
1973 /* precalculate row increments */
1974 for (x = 0; x <= dst->w; x++)
1975 *csax++ = (int)(sx * x);
1977 for (y = 0; y <= dst->h; y++)
1978 *csay++ = (int)(sy * y);
1981 sp = csp = csp0 = (tColorRGBA *) src->pixels;
1982 dp = (tColorRGBA *) dst->pixels;
1983 dgap = dst->pitch - dst->w * 4;
1986 for (y = 0; y < dst->h; y++)
1991 for (x = 0; x < dst->w; x++)
1996 /* advance source pointers */
2000 /* advance destination pointer */
2004 /* advance source pointer */
2006 csp = (tColorRGBA *) ((Uint8 *) csp0 + *csay * src->pitch);
2008 /* advance destination pointers */
2009 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
2019 -----------------------------------------------------------------------------
2022 zoomes 8 bit palette/Y 'src' surface to 'dst' surface
2023 -----------------------------------------------------------------------------
2026 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
2028 Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
2029 Uint8 *sp, *dp, *csp;
2032 /* variable setup */
2033 sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
2034 sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
2036 /* allocate memory for row increments */
2037 sax = (Uint32 *)checked_malloc(dst->w * sizeof(Uint32));
2038 say = (Uint32 *)checked_malloc(dst->h * sizeof(Uint32));
2040 /* precalculate row increments */
2043 for (x = 0; x < dst->w; x++)
2046 *csax = (csx >> 16);
2053 for (y = 0; y < dst->h; y++)
2056 *csay = (csy >> 16);
2063 for (x = 0; x < dst->w; x++)
2071 for (y = 0; y < dst->h; y++)
2078 sp = csp = (Uint8 *) src->pixels;
2079 dp = (Uint8 *) dst->pixels;
2080 dgap = dst->pitch - dst->w;
2084 for (y = 0; y < dst->h; y++)
2088 for (x = 0; x < dst->w; x++)
2093 /* advance source pointers */
2097 /* advance destination pointer */
2101 /* advance source pointer (for row) */
2102 csp += ((*csay) * src->pitch);
2105 /* advance destination pointers */
2116 -----------------------------------------------------------------------------
2119 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
2120 'zoomx' and 'zoomy' are scaling factors for width and height.
2121 If the surface is not 8bit or 32bit RGBA/ABGR it will be converted
2122 into a 32bit RGBA format on the fly.
2123 -----------------------------------------------------------------------------
2126 SDL_Surface *zoomSurface(SDL_Surface *src, int dst_width, int dst_height)
2128 SDL_Surface *zoom_src = NULL;
2129 SDL_Surface *zoom_dst = NULL;
2130 boolean is_converted = FALSE;
2137 /* determine if source surface is 32 bit or 8 bit */
2138 is_32bit = (src->format->BitsPerPixel == 32);
2140 if (is_32bit || src->format->BitsPerPixel == 8)
2142 /* use source surface 'as is' */
2147 /* new source surface is 32 bit with a defined RGB ordering */
2148 zoom_src = SDL_CreateRGBSurface(SURFACE_FLAGS, src->w, src->h, 32,
2149 0x000000ff, 0x0000ff00, 0x00ff0000,
2150 (src->format->Amask ? 0xff000000 : 0));
2151 SDL_BlitSurface(src, NULL, zoom_src, NULL);
2153 is_converted = TRUE;
2156 /* allocate surface to completely contain the zoomed surface */
2159 /* target surface is 32 bit with source RGBA/ABGR ordering */
2160 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 32,
2161 zoom_src->format->Rmask,
2162 zoom_src->format->Gmask,
2163 zoom_src->format->Bmask,
2164 zoom_src->format->Amask);
2168 /* target surface is 8 bit */
2169 zoom_dst = SDL_CreateRGBSurface(SURFACE_FLAGS, dst_width, dst_height, 8,
2173 /* lock source surface */
2174 SDL_LockSurface(zoom_src);
2176 /* check which kind of surface we have */
2179 /* call the 32 bit transformation routine to do the zooming */
2180 zoomSurfaceRGBA(zoom_src, zoom_dst);
2185 for (i = 0; i < zoom_src->format->palette->ncolors; i++)
2186 zoom_dst->format->palette->colors[i] =
2187 zoom_src->format->palette->colors[i];
2188 zoom_dst->format->palette->ncolors = zoom_src->format->palette->ncolors;
2190 /* call the 8 bit transformation routine to do the zooming */
2191 zoomSurfaceY(zoom_src, zoom_dst);
2194 /* unlock source surface */
2195 SDL_UnlockSurface(zoom_src);
2197 /* free temporary surface */
2199 SDL_FreeSurface(zoom_src);
2201 /* return destination surface */
2205 static SDL_Surface *SDLGetOpaqueSurface(SDL_Surface *surface)
2207 SDL_Surface *new_surface;
2209 if (surface == NULL)
2212 if ((new_surface = SDLGetNativeSurface(surface)) == NULL)
2213 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2215 /* remove alpha channel from native non-transparent surface, if defined */
2216 SDLSetAlpha(new_surface, FALSE, 0);
2218 /* remove transparent color from native non-transparent surface, if defined */
2219 SDL_SetColorKey(new_surface, UNSET_TRANSPARENT_PIXEL, 0);
2224 Bitmap *SDLZoomBitmap(Bitmap *src_bitmap, int dst_width, int dst_height)
2226 Bitmap *dst_bitmap = CreateBitmapStruct();
2227 SDL_Surface *src_surface = src_bitmap->surface_masked;
2228 SDL_Surface *dst_surface;
2230 dst_width = MAX(1, dst_width); /* prevent zero bitmap width */
2231 dst_height = MAX(1, dst_height); /* prevent zero bitmap height */
2233 dst_bitmap->width = dst_width;
2234 dst_bitmap->height = dst_height;
2236 /* create zoomed temporary surface from source surface */
2237 dst_surface = zoomSurface(src_surface, dst_width, dst_height);
2239 /* create native format destination surface from zoomed temporary surface */
2240 SDLSetNativeSurface(&dst_surface);
2242 /* set color key for zoomed surface from source surface, if defined */
2243 if (SDLHasColorKey(src_surface))
2244 SDL_SetColorKey(dst_surface, SET_TRANSPARENT_PIXEL,
2245 SDLGetColorKey(src_surface));
2247 /* create native non-transparent surface for opaque blitting */
2248 dst_bitmap->surface = SDLGetOpaqueSurface(dst_surface);
2250 /* set native transparent surface for masked blitting */
2251 dst_bitmap->surface_masked = dst_surface;
2257 /* ========================================================================= */
2258 /* load image to bitmap */
2259 /* ========================================================================= */
2261 Bitmap *SDLLoadImage(char *filename)
2263 Bitmap *new_bitmap = CreateBitmapStruct();
2264 SDL_Surface *sdl_image_tmp;
2266 print_timestamp_init("SDLLoadImage");
2268 print_timestamp_time(getBaseNamePtr(filename));
2270 /* load image to temporary surface */
2271 if ((sdl_image_tmp = IMG_Load(filename)) == NULL)
2272 Error(ERR_EXIT, "IMG_Load() failed: %s", SDL_GetError());
2274 print_timestamp_time("IMG_Load");
2276 UPDATE_BUSY_STATE();
2278 /* create native non-transparent surface for current image */
2279 if ((new_bitmap->surface = SDLGetOpaqueSurface(sdl_image_tmp)) == NULL)
2280 Error(ERR_EXIT, "SDLGetOpaqueSurface() failed");
2282 print_timestamp_time("SDLGetNativeSurface (opaque)");
2284 UPDATE_BUSY_STATE();
2286 /* set black pixel to transparent if no alpha channel / transparent color */
2287 if (!SDLHasAlpha(sdl_image_tmp) &&
2288 !SDLHasColorKey(sdl_image_tmp))
2289 SDL_SetColorKey(sdl_image_tmp, SET_TRANSPARENT_PIXEL,
2290 SDL_MapRGB(sdl_image_tmp->format, 0x00, 0x00, 0x00));
2292 /* create native transparent surface for current image */
2293 if ((new_bitmap->surface_masked = SDLGetNativeSurface(sdl_image_tmp)) == NULL)
2294 Error(ERR_EXIT, "SDLGetNativeSurface() failed");
2296 print_timestamp_time("SDLGetNativeSurface (masked)");
2298 UPDATE_BUSY_STATE();
2300 /* free temporary surface */
2301 SDL_FreeSurface(sdl_image_tmp);
2303 new_bitmap->width = new_bitmap->surface->w;
2304 new_bitmap->height = new_bitmap->surface->h;
2306 print_timestamp_done("SDLLoadImage");
2312 /* ------------------------------------------------------------------------- */
2313 /* custom cursor fuctions */
2314 /* ------------------------------------------------------------------------- */
2316 static SDL_Cursor *create_cursor(struct MouseCursorInfo *cursor_info)
2318 return SDL_CreateCursor(cursor_info->data, cursor_info->mask,
2319 cursor_info->width, cursor_info->height,
2320 cursor_info->hot_x, cursor_info->hot_y);
2323 void SDLSetMouseCursor(struct MouseCursorInfo *cursor_info)
2325 static struct MouseCursorInfo *last_cursor_info = NULL;
2326 static struct MouseCursorInfo *last_cursor_info2 = NULL;
2327 static SDL_Cursor *cursor_default = NULL;
2328 static SDL_Cursor *cursor_current = NULL;
2330 /* if invoked for the first time, store the SDL default cursor */
2331 if (cursor_default == NULL)
2332 cursor_default = SDL_GetCursor();
2334 /* only create new cursor if cursor info (custom only) has changed */
2335 if (cursor_info != NULL && cursor_info != last_cursor_info)
2337 cursor_current = create_cursor(cursor_info);
2338 last_cursor_info = cursor_info;
2341 /* only set new cursor if cursor info (custom or NULL) has changed */
2342 if (cursor_info != last_cursor_info2)
2343 SDL_SetCursor(cursor_info ? cursor_current : cursor_default);
2345 last_cursor_info2 = cursor_info;
2349 /* ========================================================================= */
2350 /* audio functions */
2351 /* ========================================================================= */
2353 void SDLOpenAudio(void)
2355 #if !defined(TARGET_SDL2)
2356 if (!strEqual(setup.system.sdl_audiodriver, ARG_DEFAULT))
2357 SDL_putenv(getStringCat2("SDL_AUDIODRIVER=", setup.system.sdl_audiodriver));
2360 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
2362 Error(ERR_WARN, "SDL_InitSubSystem() failed: %s", SDL_GetError());
2366 if (Mix_OpenAudio(DEFAULT_AUDIO_SAMPLE_RATE, MIX_DEFAULT_FORMAT,
2367 AUDIO_NUM_CHANNELS_STEREO,
2368 setup.system.audio_fragment_size) < 0)
2370 Error(ERR_WARN, "Mix_OpenAudio() failed: %s", SDL_GetError());
2374 audio.sound_available = TRUE;
2375 audio.music_available = TRUE;
2376 audio.loops_available = TRUE;
2377 audio.sound_enabled = TRUE;
2379 /* set number of available mixer channels */
2380 audio.num_channels = Mix_AllocateChannels(NUM_MIXER_CHANNELS);
2381 audio.music_channel = MUSIC_CHANNEL;
2382 audio.first_sound_channel = FIRST_SOUND_CHANNEL;
2384 Mixer_InitChannels();
2387 void SDLCloseAudio(void)
2390 Mix_HaltChannel(-1);
2393 SDL_QuitSubSystem(SDL_INIT_AUDIO);
2397 /* ========================================================================= */
2398 /* event functions */
2399 /* ========================================================================= */
2401 void SDLNextEvent(Event *event)
2403 SDL_WaitEvent(event);
2406 void SDLHandleWindowManagerEvent(Event *event)
2409 #if defined(PLATFORM_WIN32)
2410 // experimental drag and drop code
2412 SDL_SysWMEvent *syswmevent = (SDL_SysWMEvent *)event;
2413 SDL_SysWMmsg *syswmmsg = (SDL_SysWMmsg *)(syswmevent->msg);
2415 #if defined(TARGET_SDL2)
2416 if (syswmmsg->msg.win.msg == WM_DROPFILES)
2418 if (syswmmsg->msg == WM_DROPFILES)
2421 #if defined(TARGET_SDL2)
2422 HDROP hdrop = (HDROP)syswmmsg->msg.win.wParam;
2424 HDROP hdrop = (HDROP)syswmmsg->wParam;
2428 printf("::: SDL_SYSWMEVENT:\n");
2430 num_files = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
2432 for (i = 0; i < num_files; i++)
2434 int buffer_len = DragQueryFile(hdrop, i, NULL, 0);
2435 char buffer[buffer_len + 1];
2437 DragQueryFile(hdrop, i, buffer, buffer_len + 1);
2439 printf("::: - '%s'\n", buffer);
2442 #if defined(TARGET_SDL2)
2443 DragFinish((HDROP)syswmmsg->msg.win.wParam);
2445 DragFinish((HDROP)syswmmsg->wParam);
2453 /* ========================================================================= */
2454 /* joystick functions */
2455 /* ========================================================================= */
2457 static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL };
2458 static int sdl_js_axis[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2459 static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
2461 static boolean SDLOpenJoystick(int nr)
2463 if (nr < 0 || nr > MAX_PLAYERS)
2466 return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE);
2469 static void SDLCloseJoystick(int nr)
2471 if (nr < 0 || nr > MAX_PLAYERS)
2474 SDL_JoystickClose(sdl_joystick[nr]);
2476 sdl_joystick[nr] = NULL;
2479 static boolean SDLCheckJoystickOpened(int nr)
2481 if (nr < 0 || nr > MAX_PLAYERS)
2484 #if defined(TARGET_SDL2)
2485 return (sdl_joystick[nr] != NULL ? TRUE : FALSE);
2487 return (SDL_JoystickOpened(nr) ? TRUE : FALSE);
2491 void HandleJoystickEvent(Event *event)
2495 case SDL_JOYAXISMOTION:
2496 if (event->jaxis.axis < 2)
2497 sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value;
2500 case SDL_JOYBUTTONDOWN:
2501 if (event->jbutton.button < 2)
2502 sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE;
2505 case SDL_JOYBUTTONUP:
2506 if (event->jbutton.button < 2)
2507 sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE;
2515 void SDLInitJoysticks()
2517 static boolean sdl_joystick_subsystem_initialized = FALSE;
2518 boolean print_warning = !sdl_joystick_subsystem_initialized;
2521 if (!sdl_joystick_subsystem_initialized)
2523 sdl_joystick_subsystem_initialized = TRUE;
2525 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
2527 Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
2532 for (i = 0; i < MAX_PLAYERS; i++)
2534 /* get configured joystick for this player */
2535 char *device_name = setup.input[i].joy.device_name;
2536 int joystick_nr = getJoystickNrFromDeviceName(device_name);
2538 if (joystick_nr >= SDL_NumJoysticks())
2540 if (setup.input[i].use_joystick && print_warning)
2541 Error(ERR_WARN, "cannot find joystick %d", joystick_nr);
2546 /* misuse joystick file descriptor variable to store joystick number */
2547 joystick.fd[i] = joystick_nr;
2549 if (joystick_nr == -1)
2552 /* this allows subsequent calls to 'InitJoysticks' for re-initialization */
2553 if (SDLCheckJoystickOpened(joystick_nr))
2554 SDLCloseJoystick(joystick_nr);
2556 if (!setup.input[i].use_joystick)
2559 if (!SDLOpenJoystick(joystick_nr))
2562 Error(ERR_WARN, "cannot open joystick %d", joystick_nr);
2567 joystick.status = JOYSTICK_ACTIVATED;
2571 boolean SDLReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
2573 if (nr < 0 || nr >= MAX_PLAYERS)
2577 *x = sdl_js_axis[nr][0];
2579 *y = sdl_js_axis[nr][1];
2582 *b1 = sdl_js_button[nr][0];
2584 *b2 = sdl_js_button[nr][1];