From db01547ae65dbf04d93ac93a526573c3f819bd5b Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 11 Jun 2016 03:06:30 +0100 Subject: [PATCH] implement OpenGL screenshot --- .../engines/opengl/OpenGLDrawingEngine.cpp | 8 ++- .../engines/opengl/OpenGLFramebuffer.cpp | 22 +++++++ .../engines/opengl/OpenGLFramebuffer.h | 1 + src/image_io.c | 61 +++++++++++++++++++ src/image_io.h | 1 + src/interface/screenshot.c | 16 +++++ src/interface/screenshot.h | 1 + 7 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp index fc1bd08188..8b0fd9dfe7 100644 --- a/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp +++ b/src/drawing/engines/opengl/OpenGLDrawingEngine.cpp @@ -47,6 +47,7 @@ IDrawingEngine * DrawingEngineFactory::CreateOpenGL() extern "C" { #include "../../../config.h" + #include "../../../interface/screenshot.h" #include "../../../interface/window.h" #include "../../../intro.h" #include "../../drawing.h" @@ -328,8 +329,11 @@ public: sint32 Screenshot() override { - // Not implemented - return -1; + _canvasFramebuffer->Bind(); + void * pixels = _canvasFramebuffer->GetPixels(); + int result = screenshot_dump_png_32bpp(_width, _height, pixels); + Memory::Free(pixels); + return result; } void CopyRect(sint32 x, sint32 y, sint32 width, sint32 height, sint32 dx, sint32 dy) override diff --git a/src/drawing/engines/opengl/OpenGLFramebuffer.cpp b/src/drawing/engines/opengl/OpenGLFramebuffer.cpp index 9f5315349e..5cab9df4ae 100644 --- a/src/drawing/engines/opengl/OpenGLFramebuffer.cpp +++ b/src/drawing/engines/opengl/OpenGLFramebuffer.cpp @@ -15,6 +15,7 @@ #pragma endregion #include +#include "../../../core/Memory.hpp" #include "OpenGLFramebuffer.h" constexpr GLuint BACKBUFFER_ID = 0; @@ -56,3 +57,24 @@ void OpenGLFramebuffer::Bind() glBindFramebuffer(GL_FRAMEBUFFER, _id); glViewport(0, 0, (GLsizei)_width, (GLsizei)_height); } + +void * OpenGLFramebuffer::GetPixels() const +{ + void * pixels = Memory::Allocate(_width * _height * 4); + glReadPixels(0, 0, _width, _height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Flip pixels vertically + void * flippedPixels = Memory::Allocate(_width * _height * 4); + size_t stride = _width * 4; + uint8 * src = (uint8 *)pixels + ((_height - 1) * stride); + uint8 * dst = (uint8 *)flippedPixels; + for (sint32 y = 0; y < _height; y++) + { + Memory::Copy(dst, src, stride); + src -= stride; + dst += stride; + } + Memory::Free(pixels); + + return flippedPixels; +} diff --git a/src/drawing/engines/opengl/OpenGLFramebuffer.h b/src/drawing/engines/opengl/OpenGLFramebuffer.h index cd27d02076..bab36dd67d 100644 --- a/src/drawing/engines/opengl/OpenGLFramebuffer.h +++ b/src/drawing/engines/opengl/OpenGLFramebuffer.h @@ -39,4 +39,5 @@ public: GLuint GetTexture() const { return _texture; } void Bind(); + void * GetPixels() const; }; diff --git a/src/image_io.c b/src/image_io.c index f897a20c88..b1909308b0 100644 --- a/src/image_io.c +++ b/src/image_io.c @@ -171,6 +171,67 @@ bool image_io_png_write(const rct_drawpixelinfo *dpi, const rct_palette *palette return true; } +static void image_io_png_warning(png_structp png_ptr, const char *b) +{ + log_warning(b); +} + +static void image_io_png_error(png_structp png_ptr, const char *b) +{ + log_error(b); +} + +bool image_io_png_write_32bpp(sint32 width, sint32 height, const void *pixels, const utf8 *path) +{ + // Setup PNG + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, image_io_png_error, image_io_png_warning); + if (png_ptr == NULL) { + return false; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return false; + } + + // Open file for writing + SDL_RWops *file = SDL_RWFromFile(path, "wb"); + if (file == NULL) { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return false; + } + png_set_write_fn(png_ptr, file, my_png_write_data, my_png_flush); + + // Set error handler + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + SDL_RWclose(file); + return false; + } + + // Write header + png_set_IHDR( + png_ptr, info_ptr, width, height, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT + ); + png_write_info(png_ptr, info_ptr); + + // Write pixels + uint8 *bits = (uint8*)pixels; + for (int y = 0; y < height; y++) { + png_write_row(png_ptr, (png_byte *)bits); + bits += width * 4; + } + + // Finish + png_write_end(png_ptr, NULL); + SDL_RWclose(file); + + png_destroy_write_struct(&png_ptr, &info_ptr); + return true; +} + static void my_png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { SDL_RWops *file = (SDL_RWops*)png_get_io_ptr(png_ptr); diff --git a/src/image_io.h b/src/image_io.h index 6e920963fc..82a45a91b4 100644 --- a/src/image_io.h +++ b/src/image_io.h @@ -23,5 +23,6 @@ bool image_io_png_read(uint8 **pixels, uint32 *width, uint32 *height, const utf8 *path); bool image_io_png_write(const rct_drawpixelinfo *dpi, const rct_palette *palette, const utf8 *path); +bool image_io_png_write_32bpp(sint32 width, sint32 height, const void *pixels, const utf8 *path); #endif diff --git a/src/interface/screenshot.c b/src/interface/screenshot.c index 7ba9a5bc02..19ed7ff9bd 100644 --- a/src/interface/screenshot.c +++ b/src/interface/screenshot.c @@ -115,6 +115,22 @@ int screenshot_dump_png(rct_drawpixelinfo *dpi) } } +int screenshot_dump_png_32bpp(sint32 width, sint32 height, const void *pixels) +{ + // Get a free screenshot path + int index; + char path[MAX_PATH] = ""; + if ((index = screenshot_get_next_path(path)) == -1) { + return -1; + } + + if (image_io_png_write_32bpp(width, height, pixels, path)) { + return index; + } else { + return -1; + } +} + void screenshot_giant() { int originalRotation = get_current_rotation(); diff --git a/src/interface/screenshot.h b/src/interface/screenshot.h index 5b14a788d8..f29d54515c 100644 --- a/src/interface/screenshot.h +++ b/src/interface/screenshot.h @@ -22,6 +22,7 @@ void screenshot_check(); int screenshot_dump(); int screenshot_dump_png(rct_drawpixelinfo *dpi); +int screenshot_dump_png_32bpp(sint32 width, sint32 height, const void *pixels); void screenshot_giant(); int cmdline_for_screenshot(const char **argv, int argc);