/** * Layer to display surfaces using SDL. */ #ifdef HAVE_SDL #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include static SDL_Surface *screen = NULL; static struct visual_ops sdl_layer_ops = { .quit = visual_sdl_quit, .init = visual_sdl_init, .update = visual_sdl_update, .waitkey = visual_sdl_waitkey, .clear = visual_sdl_clear, .fill = visual_sdl_fill, .display = visual_sdl_display, .save = visual_sdl_save, }; /** * Change visual layer to use SDL. */ void visual_change_layer_sdl( void ) { visual_change_layer( &sdl_layer_ops ); } /** * Quit engine. */ void visual_sdl_quit( void ) { SDL_Quit(); } /** * Initialize video device using the given dimensions. * * It ouput video information using info(), see debug.h to set the right * debug_level to see this info. * * @param w screen width * @param h screen height * * @return 0 on succes, error otherwise. */ int visual_sdl_init( uint16_t w, uint16_t h ) { if ( SDL_Init( SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE ) != 0) { err( "Could not initialize SDL: %s\n", SDL_GetError() ); return -1; } atexit( visual_sdl_quit ); const SDL_VideoInfo *i = SDL_GetVideoInfo(); if ( i == NULL ) return -1; /* Print out video info */ info( "Video Information:\n" ); info( " Hardware Acceleration:\n" ); info( " available......? %s\n", i->hw_available ? "Yes" : "No" ); info( " blit...........? %s\n", i->blit_hw ? "Yes" : "No" ); info( " blit color key.? %s\n", i->blit_hw_CC ? "Yes" : "No" ); info( " blit alpha.....? %s\n", i->blit_hw_A ? "Yes" : "No" ); info( " Software Acceleration:\n" ); info( " blit...........? %s\n", i->blit_sw ? "Yes" : "No" ); info( " blit color key.? %s\n", i->blit_sw_CC ? "Yes" : "No" ); info( " blit alpha.....? %s\n", i->blit_sw_A ? "Yes" : "No" ); info( "\n" ); info( " blit_fill? %s\n", i->blit_fill ? "Yes" : "No" ); info( " video_mem: %d Kb\n", i->video_mem ); info( "\n" ); SDL_PixelFormat *pf = i->vfmt; info( "Best Pixel Format:\n" ); info( " BitsPerPixel: %d (bytes: %d)\n", pf->BitsPerPixel, pf->BytesPerPixel ); uint8_t bpp = 2 << sizeof(color_t); screen = SDL_SetVideoMode( w, h, bpp, SDL_SWSURFACE ); if ( screen == NULL ) { err( "Could not create screen %dx%d @ %dbpp.\n", w, h, bpp ); return -1; } info( "Allocated screen %dx%d @ %dbpp.\n", w, h, bpp ); return 0; } /** * Update screen. * * @return 0 on success, error otherwise. */ int visual_sdl_update( void ) { if ( screen == NULL ) return -1; return SDL_Flip( screen ); } /** * Wait for key and return key symbol as described in SDLKey(3). * * It also handle program exit (SDL_QUIT) and do screen refreshes when * required (SDL_ACTIVEEVENT). * * @return key symbol or 0 on error. */ int visual_sdl_waitkey( void ) { SDL_Event e; while ( 1 ) { SDL_WaitEvent( &e ); switch ( e.type ) { case SDL_KEYDOWN: /* ignore keydown */ break; case SDL_KEYUP: return e.key.keysym.sym; break; case SDL_ACTIVEEVENT: visual_sdl_update(); break; case SDL_QUIT: exit( 0 ); break; default: break; } } return 0; } /** * Clear (blanks) screen. * * @return 0 on success, error otherwise. */ int visual_sdl_clear( void ) { if ( screen == NULL ) return -1; return SDL_FillRect( screen, NULL, 0 ); } /** * Fill rectangular screen region with the given color. * * @param color what color to paint. * @param rectan * * @return 0 on success, error otherwise. */ int visual_sdl_fill( color_t color, rect_t rect ) { if ( screen == NULL ) return -1; SDL_Rect r = { rect.p0.x, rect.p0.y, rect.p1.x - rect.p0.x, rect.p1.y - rect.p0.y }; return SDL_FillRect( screen, &r, SDL_MapRGBA( screen->format, color.r, color.g, color.b, color.a ) ); } /** * Display the surface at the given position * * @param surface what to display * @param pos where to display * * @return 0 on success, error otherwise. */ int visual_sdl_display( surface_t *surface, coord_t pos ) { if ( screen == NULL ) return -1; /* Check if it's outside screen */ if ( ( pos.x > screen->w ) || ( pos.y > screen->h ) ) return -1; /* Limit drawing to inside screen */ uint16_t x_max = min( surface->size.x, (uint16_t)screen->w ); uint16_t y_max = min( surface->size.y, (uint16_t)screen->h ); /* Ensure SDL surface is same depth as our color_t */ uint_fast8_t bpp = sizeof(color_t); assert( bpp == screen->format->BytesPerPixel ); /* setup pointers */ color_t *src = &(PIXEL_AT( surface, 0, 0 )); color_t *dst = &((color_t*)screen->pixels)[ \ QUAD_TO_LIN( screen->w, pos.x, pos.y ) ]; uint16_t dy = y_max - pos.y; uint16_t dx = x_max - pos.x; uint16_t dx_bytes = dx * bpp; /* copy src to dst */ uint_fast16_t i; for ( i=0; i < dy; i++ ) { memcpy( dst, src, dx_bytes ); /* this is fast! */ /* go to next line */ src = src + surface->size.x; dst = dst + screen->w; } return 0; } /** * Save the display to a bitmap image. * * @param filename where to save the file. * * @return 0 on success, error otherwise. */ int visual_sdl_save( const char *filename ) { return SDL_SaveBMP( screen, filename ); } #endif /* HAVE_SDL */