/** * Layer to display surfaces using SDL. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include static surface_t *screen = NULL; static struct visual_ops ppm_layer_ops = { .quit = visual_ppm_quit, .init = visual_ppm_init, .update = visual_ppm_update, .waitkey = visual_ppm_waitkey, .clear = visual_ppm_clear, .fill = visual_ppm_fill, .display = visual_ppm_display, .save = visual_ppm_save, }; #define DEFAULT_FILENAME "output" #define DEFAULT_SUFFIX "ppm" static char * find_fname( const char *prefix, const char *suffix ) { struct stat s; unsigned i = 1; char *n = NULL; do { if ( n != NULL ) free( n ); asprintf( &n, "%s-%05d.%s", prefix, i++, suffix ); } while ( stat( n, &s ) == 0 ); return n; } static int ppm_saveimage( const char *filename ) { if ( screen == NULL ) return -1; FILE *fd = fopen( filename, "w" ); if ( fd == NULL ) { perror( "Could not open file" ); return -1; } uint16_t w = screen->size.x; uint16_t h = screen->size.y; fprintf( fd, "P6\n%d %d\n255\n", w, h ); color_t *p, *end = screen->buffer + ( w * h ); for ( p=screen->buffer; p < end; p++ ) fprintf( fd, "%c%c%c", p->r, p->g, p->b ); fclose( fd ); return 0; } /** * Change visual layer to use PPM. */ void visual_change_layer_ppm( void ) { visual_change_layer( &ppm_layer_ops ); } /** * Quit engine. */ void visual_ppm_quit( void ) { if ( screen != NULL ) surface_free( screen ); } /** * 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_ppm_init( uint16_t w, uint16_t h ) { screen = surface_new( w, h ); return screen == NULL; } /** * Update screen. * * @return 0 on success, error otherwise. */ int visual_ppm_update( void ) { if ( screen == NULL ) return -1; char *f = find_fname( DEFAULT_FILENAME, DEFAULT_SUFFIX ); int r; if ( ( r = ppm_saveimage( f ) ) == 0 ) printf( "Image saved to: \"%s\"\n", f ); free( f ); return r; } /** * 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_ppm_waitkey( void ) { return 0; } /** * Clear (blanks) screen. * * @return 0 on success, error otherwise. */ int visual_ppm_clear( void ) { if ( screen == NULL ) return -1; surface_clear( screen ); return 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_ppm_fill( color_t color, rect_t rect ) { if ( screen == NULL ) return -1; surface_fill( screen, color, rect ); return 0; } /** * 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_ppm_display( surface_t *surface, coord_t pos ) { if ( screen == NULL ) return -1; /* Check if it's outside screen */ if ( ( pos.x > screen->size.x ) || ( pos.y > screen->size.y ) ) return -1; /* Limit drawing to inside screen */ uint16_t x_max = min( surface->size.x, (uint16_t)screen->size.x ); uint16_t y_max = min( surface->size.y, (uint16_t)screen->size.y ); /* Ensure SDL surface is same depth as our color_t */ uint_fast8_t bpp = sizeof(color_t); /* setup pointers */ color_t *src = &(PIXEL_AT( surface, 0, 0 )); color_t *dst = &(PIXEL_AT( screen, 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->size.x; } return 0; } /** * Save the display to a bitmap image. * * @param filename where to save the file. * * @return 0 on success, error otherwise. */ int visual_ppm_save( const char *filename ) { return ppm_saveimage( filename ); }