diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ad9e89a0..9508ac193 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ endif() source_group("ARM debugger" FILES ${DEBUGGER_SRC}) add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${VFS_SRC} ${OS_SRC}) -target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB}) +target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB} png) if(BUILD_SDL) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl) diff --git a/src/platform/sdl/gl-main.c b/src/platform/sdl/gl-main.c index f494366ea..23d358bea 100644 --- a/src/platform/sdl/gl-main.c +++ b/src/platform/sdl/gl-main.c @@ -77,6 +77,7 @@ int main(int argc, char** argv) { renderer.events.fullscreen = graphicsOpts.fullscreen; renderer.events.windowUpdated = 0; #endif + renderer.events.renderer = &renderer.d; if (!_GBASDLInit(&renderer)) { freeOptions(&opts); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index c087a8b6d..b0c15ce3a 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -5,6 +5,9 @@ #include "gba-rr.h" #include "gba-serialize.h" #include "gba-video.h" +#include "renderers/video-software.h" +#include "util/png-io.h" +#include "util/vfs.h" #if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__) #define GUI_MOD KMOD_GUI @@ -15,6 +18,8 @@ #define SDL_BINDING_KEY 0x53444C4B #define SDL_BINDING_BUTTON 0x53444C42 +static void _takeScreenshot(struct GBAThread*, struct GBASDLEvents*); + bool GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { return false; @@ -74,6 +79,13 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL); } return; + case SDLK_F12: + if (event->type == SDL_KEYDOWN) { + GBAThreadInterrupt(context); + _takeScreenshot(context, sdlContext); + GBAThreadContinue(context); + } + return; case SDLK_TAB: context->sync.audioWait = event->type != SDL_KEYDOWN; return; @@ -249,3 +261,12 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContex _GBASDLHandleJoyHat(context, &event->jhat); } } + +static void _takeScreenshot(struct GBAThread* context, struct GBASDLEvents* sdlContext) { + struct VFile* vf = context->stateDir->openFile(context->stateDir, "screenshot.png", O_CREAT | O_WRONLY); + png_structp png = PNGWriteOpen(vf); + png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); + PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 256, sdlContext->renderer->outputBuffer); + PNGWriteClose(png, info); + vf->close(vf); +} diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 1621aa052..fe53a1b1a 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -7,8 +7,11 @@ #include +struct GBAVideoSoftwareRenderer; + struct GBASDLEvents { struct GBAInputMap* bindings; + struct GBAVideoSoftwareRenderer* renderer; SDL_Joystick* joystick; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; diff --git a/src/util/png-io.c b/src/util/png-io.c new file mode 100644 index 000000000..dd9439433 --- /dev/null +++ b/src/util/png-io.c @@ -0,0 +1,76 @@ +#include "util/png-io.h" + +#include "vfs.h" + +static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) { + struct VFile* vf = png_get_io_ptr(png); + size_t written = vf->write(vf, buffer, size); + if (written != size) { + png_error(png, "Could not write PNG"); + } +} + +png_structp PNGWriteOpen(struct VFile* source) { + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png) { + return 0; + } + if (setjmp(png_jmpbuf(png))) { + png_destroy_write_struct(&png, 0); + return 0; + } + png_set_write_fn(png, source, _pngWrite, 0); + return png; +} + +png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) { + png_infop info = png_create_info_struct(png); + if (!info) { + return 0; + } + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_write_info(png, info); + return info; +} + +bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, void* pixels) { + png_bytep row = malloc(sizeof(png_bytep) * width * 3); + if (!row) { + return false; + } + png_bytep pixelData = pixels; + if (setjmp(png_jmpbuf(png))) { + free(row); + return false; + } + unsigned i; + for (i = 0; i < height; ++i) { + unsigned x; + for (x = 0; x < width; ++x) { + row[x * 3] = pixelData[stride * i * 4 + x * 4]; + row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 1]; + row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 2]; + } + png_write_row(png, row); + } + free(row); + return true; +} + +bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) { + char realName[5]; + strncpy(realName, name, 4); + realName[0] = tolower(realName[0]); + realName[1] = tolower(realName[1]); + realName[4] = '\0'; + if (setjmp(png_jmpbuf(png))) { + return false; + } + png_write_chunk(png, (png_const_bytep) realName, data, size); + return true; +} + +void PNGWriteClose(png_structp png, png_infop info) { + png_write_end(png, info); + png_destroy_write_struct(&png, &info); +} diff --git a/src/util/png-io.h b/src/util/png-io.h new file mode 100644 index 000000000..2d3f0ebbc --- /dev/null +++ b/src/util/png-io.h @@ -0,0 +1,16 @@ +#ifndef PNG_IO_H +#define PNG_IO_H + +#include "common.h" + +#include + +struct VFile; + +png_structp PNGWriteOpen(struct VFile* source); +png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height); +bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, void* pixels); +bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data); +void PNGWriteClose(png_structp png, png_infop info); + +#endif