From 4921d92650f71f08ca56ca6ca122df200dae9d70 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Sun, 27 Sep 2015 16:14:55 -0700 Subject: [PATCH] VIDEO: Produce SCREENSHOT images in PNG format if libpng is available in the host build environment --- Visual Studio Projects/MicroVAX1.vcproj | 16 +- Visual Studio Projects/MicroVAX2.vcproj | 16 +- Visual Studio Projects/Pre-Build-Event.cmd | 1 + Visual Studio Projects/VAX.vcproj | 16 +- scp.c | 7 +- sim_video.c | 201 ++++++++++++++++++++- 6 files changed, 229 insertions(+), 28 deletions(-) diff --git a/Visual Studio Projects/MicroVAX1.vcproj b/Visual Studio Projects/MicroVAX1.vcproj index b813e99b..706d5927 100644 --- a/Visual Studio Projects/MicroVAX1.vcproj +++ b/Visual Studio Projects/MicroVAX1.vcproj @@ -41,8 +41,8 @@ = 1900" ..\..\windows-build\pthreads\pthread.h >NUL if ERRORLEVEL 1 goto _notice2 if "%_X_LIBSDL%" == "" goto _done_libsdl +if not exist ../../windows-build/libpng* goto _notice2 if not exist ../../windows-build/libSDL/SDL2-2.0.3/include/SDL.h goto _notice2 if not exist "../../windows-build/libSDL/Microsoft DirectX SDK (June 2010)/Lib/x86/dxguid.lib" goto _notice2 findstr "/c:HAVE_FTOL2_SSE" ..\..\windows-build\libSDL\SDL2-2.0.3\VisualC\SDL_Static\SDL_VS2008.vcproj >NUL diff --git a/Visual Studio Projects/VAX.vcproj b/Visual Studio Projects/VAX.vcproj index a6f34fb1..001c5de6 100644 --- a/Visual Studio Projects/VAX.vcproj +++ b/Visual Studio Projects/VAX.vcproj @@ -41,8 +41,8 @@ #include +#if defined(HAVE_LIBPNG) +/* From: https://github.com/driedfruit/SDL_SavePNG */ + +/* + * Save an SDL_Surface as a PNG file. + * + * Returns 0 success or -1 on failure, the error message is then retrievable + * via SDL_GetError(). + */ +#define SDL_SavePNG(surface, file) \ + SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1) + +/* + * SDL_SavePNG -- libpng-based SDL_Surface writer. + * + * This code is free software, available under zlib/libpng license. + * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt + */ +#include +#include + +#define SUCCESS 0 +#define ERROR -1 + +#define USE_ROW_POINTERS + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xFF000000 +#define gmask 0x00FF0000 +#define bmask 0x0000FF00 +#define amask 0x000000FF +#else +#define rmask 0x000000FF +#define gmask 0x0000FF00 +#define bmask 0x00FF0000 +#define amask 0xFF000000 +#endif + +/* libpng callbacks */ +static void png_error_SDL(png_structp ctx, png_const_charp str) +{ + SDL_SetError("libpng: %s\n", str); +} +static void png_write_SDL(png_structp png_ptr, png_bytep data, png_size_t length) +{ + SDL_RWops *rw = (SDL_RWops*)png_get_io_ptr(png_ptr); + SDL_RWwrite(rw, data, sizeof(png_byte), length); +} + +static SDL_Surface *SDL_PNGFormatAlpha(SDL_Surface *src) +{ + SDL_Surface *surf; + SDL_Rect rect = { 0 }; + + /* NO-OP for images < 32bpp and 32bpp images that already have Alpha channel */ + if (src->format->BitsPerPixel <= 24 || src->format->Amask) { + src->refcount++; + return src; + } + + /* Convert 32bpp alpha-less image to 24bpp alpha-less image */ + rect.w = src->w; + rect.h = src->h; + surf = SDL_CreateRGBSurface(src->flags, src->w, src->h, 24, + src->format->Rmask, src->format->Gmask, src->format->Bmask, 0); + SDL_LowerBlit(src, &rect, surf, &rect); + + return surf; +} + +static int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) +{ + png_structp png_ptr; + png_infop info_ptr; + png_colorp pal_ptr; + SDL_Palette *pal; + int i, colortype; +#ifdef USE_ROW_POINTERS + png_bytep *row_pointers; +#endif + /* Initialize and do basic error checking */ + if (!dst) + { + SDL_SetError("Argument 2 to SDL_SavePNG_RW can't be NULL, expecting SDL_RWops*\n"); + return (ERROR); + } + if (!surface) + { + SDL_SetError("Argument 1 to SDL_SavePNG_RW can't be NULL, expecting SDL_Surface*\n"); + if (freedst) SDL_RWclose(dst); + return (ERROR); + } + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_SDL, NULL); /* err_ptr, err_fn, warn_fn */ + if (!png_ptr) + { + SDL_SetError("Unable to png_create_write_struct on %s\n", PNG_LIBPNG_VER_STRING); + if (freedst) SDL_RWclose(dst); + return (ERROR); + } + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + SDL_SetError("Unable to png_create_info_struct\n"); + png_destroy_write_struct(&png_ptr, NULL); + if (freedst) SDL_RWclose(dst); + return (ERROR); + } + if (setjmp(png_jmpbuf(png_ptr))) /* All other errors, see also "png_error_SDL" */ + { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (freedst) SDL_RWclose(dst); + return (ERROR); + } + + /* Setup our RWops writer */ + png_set_write_fn(png_ptr, dst, png_write_SDL, NULL); /* w_ptr, write_fn, flush_fn */ + + /* Prepare chunks */ + colortype = PNG_COLOR_MASK_COLOR; + if (surface->format->BytesPerPixel > 0 + && surface->format->BytesPerPixel <= 8 + && (pal = surface->format->palette)) + { + colortype |= PNG_COLOR_MASK_PALETTE; + pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color)); + for (i = 0; i < pal->ncolors; i++) { + pal_ptr[i].red = pal->colors[i].r; + pal_ptr[i].green = pal->colors[i].g; + pal_ptr[i].blue = pal->colors[i].b; + } + png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors); + free(pal_ptr); + } + else if (surface->format->BytesPerPixel > 3 || surface->format->Amask) + colortype |= PNG_COLOR_MASK_ALPHA; + + png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, colortype, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + +// png_set_packing(png_ptr); + + /* Allow BGR surfaces */ + if (surface->format->Rmask == bmask + && surface->format->Gmask == gmask + && surface->format->Bmask == rmask) + png_set_bgr(png_ptr); + + /* Write everything */ + png_write_info(png_ptr, info_ptr); +#ifdef USE_ROW_POINTERS + row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surface->h); + for (i = 0; i < surface->h; i++) + row_pointers[i] = (png_bytep)(Uint8*)surface->pixels + i * surface->pitch; + png_write_image(png_ptr, row_pointers); + free(row_pointers); +#else + for (i = 0; i < surface->h; i++) + png_write_row(png_ptr, (png_bytep)(Uint8*)surface->pixels + i * surface->pitch); +#endif + png_write_end(png_ptr, info_ptr); + + /* Done */ + png_destroy_write_struct(&png_ptr, &info_ptr); + if (freedst) SDL_RWclose(dst); + return (SUCCESS); +} +#endif /* defined(HAVE_LIBPNG) */ + /* Some platforms (OS X), require that ALL input event processing be performed by the main thread of the process. @@ -1840,22 +2009,48 @@ return _show_stat; static t_stat _vid_screenshot (const char *filename) { +int stat; +char *fullname = NULL; + if (!vid_active) { sim_printf ("No video display is active\n"); return SCPE_UDIS | SCPE_NOMESSAGE; } +fullname = malloc (strlen(filename) + 5); #if SDL_MAJOR_VERSION == 1 -SDL_SaveBMP(vid_image, filename); +#if defined(HAVE_LIBPNG) +sprintf (fullname, "%s.png", filename); +stat = SDL_SavePNG(vid_image, fullname); #else +sprintf (fullname, "%s.bmp", filename); +stat = SDL_SaveBMP(vid_image, fullname); +#endif /* defined(HAVE_LIBPNG) */ +#else /* SDL_MAJOR_VERSION != 1 */ if (1) { SDL_Surface *sshot = sim_end ? SDL_CreateRGBSurface(0, vid_width, vid_height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000) : SDL_CreateRGBSurface(0, vid_width, vid_height, 32, 0x0000ff00, 0x000ff000, 0xff000000, 0x000000ff) ; SDL_RenderReadPixels(vid_renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch); - SDL_SaveBMP(sshot, filename); +#if defined(HAVE_LIBPNG) + sprintf (fullname, "%s.png", filename); + stat = SDL_SavePNG(sshot, fullname); +#else + sprintf (fullname, "%s.bmp", filename); + stat = SDL_SaveBMP(sshot, fullname); +#endif /* defined(HAVE_LIBPNG) */ SDL_FreeSurface(sshot); } #endif -return SCPE_OK; +if (stat) { + sim_printf ("Error saving screenshot to %s: %s\n", fullname, SDL_GetError()); + free (fullname); + return SCPE_IOERR | SCPE_NOMESSAGE; + } +else { + if (!sim_quiet) + sim_printf ("Screenshot saved to %s\n", fullname); + free (fullname); + return SCPE_OK; + } } static t_stat _screenshot_stat;