From cdb0230bc75d93f82334cde043a6e16fa01c8d46 Mon Sep 17 00:00:00 2001 From: Steve Date: Fri, 9 Mar 2018 18:26:33 +0000 Subject: [PATCH] Save images as PNG (can be a bit slow). --- common.mk | 6 +- makefile | 2 +- src/system/draw.c | 6 +- src/system/draw.h | 1 + src/system/savepng.c | 154 +++++++++++++++++++++++++++++++++++++++++++ src/system/savepng.h | 45 +++++++++++++ 6 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 src/system/savepng.c create mode 100644 src/system/savepng.h diff --git a/common.mk b/common.mk index 1b04e26..458b6f3 100644 --- a/common.mk +++ b/common.mk @@ -30,7 +30,7 @@ vpath %.h $(SEARCHPATH) DEPS += defs.h structs.h -_OBJS += atlas.o atlasTest.o aquaBlob.o +_OBJS += atlas.o aquaBlob.o _OBJS += battery.o blaze.o bob.o boss.o blobBoss.o bullet.o _OBJS += camera.o cannon.o cardReader.o cell.o cherry.o combat.o controls.o consumable.o _OBJS += debris.o destructable.o door.o draw.o @@ -47,10 +47,10 @@ _OBJS += objectives.o options.o _OBJS += particles.o player.o plasmaBlob.o plasmaDroid.o pistolBlob.o pistolDroid.o postMission.o powerPoint.o powerPool.o pressurePlate.o pushBlock.o _OBJS += quadtree.o _OBJS += radar.o -_OBJS += shotgunBlob.o shotgunDroid.o sound.o spreadGunBlob.o spreadGunDroid.o sprites.o stats.o structures.o +_OBJS += savepng.o shotgunBlob.o shotgunDroid.o sound.o spreadGunBlob.o spreadGunDroid.o sprites.o stats.o structures.o _OBJS += tankCommander.o tankTrack.o teeka.o teleporter.o text.o textures.o title.o transition.o triggers.o trophies.o _OBJS += unit.o util.o -_OBJS += weapons.o weaponPickup.o widgets.o world.o worldLoader.o worldSaver.o +_OBJS += weapons.o weaponPickup.o widgets.o world.o worldLoader.o worldSaver.o worldTest.o OBJS = $(patsubst %,$(OUT)/%,$(_OBJS)) diff --git a/makefile b/makefile index 59fd730..1c65c4d 100644 --- a/makefile +++ b/makefile @@ -25,7 +25,7 @@ CXXFLAGS += -Wall -Wempty-body -ansi -pedantic -Werror -Wstrict-prototypes -Werr CXXFLAGS += -g -lefence CXXFLAGS += -fms-extensions -std=gnu11 -LDFLAGS += `sdl2-config --libs` -lSDL2_mixer -lSDL2_image -lSDL2_ttf -lm -lz +LDFLAGS += `sdl2-config --libs` -lSDL2_mixer -lSDL2_image -lSDL2_ttf -lm -lz -lpng SHARED_FILES = CHANGELOG LICENSE README.md data gfx manual music sound icons DIST_FILES = $(SHARED_FILES) locale $(PROG) diff --git a/src/system/draw.c b/src/system/draw.c index 918a865..94611f7 100644 --- a/src/system/draw.c +++ b/src/system/draw.c @@ -266,15 +266,15 @@ void saveScreenshot(char *name) if (name != NULL) { - sprintf(filename, "%s/%s.bmp", app.saveDir, name); + sprintf(filename, "%s/%s.png", app.saveDir, name); } else { - sprintf(filename, "%s/%d.bmp", dev.screenshotFolder, SDL_GetTicks()); + sprintf(filename, "%s/%d.png", dev.screenshotFolder, SDL_GetTicks()); } screenshot = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); SDL_RenderReadPixels(app.renderer, NULL, SDL_PIXELFORMAT_ARGB8888, screenshot->pixels, screenshot->pitch); - SDL_SaveBMP(screenshot, filename); + SDL_SavePNG(screenshot, filename); SDL_FreeSurface(screenshot); } diff --git a/src/system/draw.h b/src/system/draw.h index 91661cd..f7db280 100644 --- a/src/system/draw.h +++ b/src/system/draw.h @@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "../common.h" +#include "savepng.h" extern void drawText(int x, int y, int size, int align, SDL_Color c, const char *format, ...); diff --git a/src/system/savepng.c b/src/system/savepng.c new file mode 100644 index 0000000..4ac44a9 --- /dev/null +++ b/src/system/savepng.c @@ -0,0 +1,154 @@ +/* + * 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); +} + +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; +} + +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); +} diff --git a/src/system/savepng.h b/src/system/savepng.h new file mode 100644 index 0000000..5942466 --- /dev/null +++ b/src/system/savepng.h @@ -0,0 +1,45 @@ +#ifndef _SDL_SAVEPNG +#define _SDL_SAVEPNG +/* + * 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 + +#ifdef __cplusplus +extern "C" { /* This helps CPP projects that include this header */ +#endif + +/* + * 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) + +/* + * Save an SDL_Surface as a PNG file, using writable RWops. + * + * surface - the SDL_Surface structure containing the image to be saved + * dst - a data stream to save to + * freedst - non-zero to close the stream after being written + * + * Returns 0 success or -1 on failure, the error message is then retrievable + * via SDL_GetError(). + */ +extern int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *rw, int freedst); + +/* + * Return new SDL_Surface with a format suitable for PNG output. + */ +extern SDL_Surface *SDL_PNGFormatAlpha(SDL_Surface *src); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif