From fa0bb7dd5a60c8f323ecbd5e190ad705bec3e934 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Fri, 12 May 2017 12:44:44 -0500 Subject: BACKENDS: Compress screenshots using PNG if available Closes gh-948. --- backends/graphics/opengl/opengl-graphics.cpp | 40 +++++++------ backends/graphics/openglsdl/openglsdl-graphics.cpp | 4 ++ .../graphics/surfacesdl/surfacesdl-graphics.cpp | 68 +++++++++++++++++++++- 3 files changed, 93 insertions(+), 19 deletions(-) (limited to 'backends/graphics') diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index a595d076db..fc27270648 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -28,6 +28,7 @@ #include "backends/graphics/opengl/pipelines/shader.h" #include "backends/graphics/opengl/shader.h" +#include "common/array.h" #include "common/textconsole.h" #include "common/translation.h" #include "common/algorithm.h" @@ -43,6 +44,10 @@ #include "graphics/font.h" #endif +#ifdef USE_PNG +#include "image/png.h" +#endif + namespace OpenGL { OpenGLGraphicsManager::OpenGLGraphicsManager() @@ -1311,33 +1316,35 @@ bool OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const // Since we use a 3 byte per pixel mode, we can use width % 4 here, since // it is equal to 4 - (width * 3) % 4. (4 - (width * Bpp) % 4, is the // usual way of computing the padding bytes required). + // GL_PACK_ALIGNMENT is 4, so this line padding is required for PNG too const uint linePaddingSize = width % 4; const uint lineSize = width * 3 + linePaddingSize; - // Allocate memory for screenshot - uint8 *pixels = new uint8[lineSize * height]; + Common::DumpFile out; + if (!out.open(filename)) { + return false; + } - // Get pixel data from OpenGL buffer - GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels)); + Common::Array pixels; + pixels.resize(lineSize * height); + GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels.front())); +#ifdef USE_PNG + const Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0); + Graphics::Surface data; + data.init(width, height, lineSize, &pixels.front(), format); + return Image::writePNG(out, data, true); +#else // BMP stores as BGR. Since we can't assume that GL_BGR is supported we // will swap the components from the RGB we read to BGR on our own. for (uint y = height; y-- > 0;) { - uint8 *line = pixels + y * lineSize; + uint8 *line = &pixels.front() + y * lineSize; for (uint x = width; x > 0; --x, line += 3) { SWAP(line[0], line[2]); } } - // Open file - Common::DumpFile out; - if (!out.open(filename)) { - delete[] pixels; - return false; - } - - // Write BMP header out.writeByte('B'); out.writeByte('M'); out.writeUint32LE(height * lineSize + 54); @@ -1354,13 +1361,10 @@ bool OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const out.writeUint32LE(0); out.writeUint32LE(0); out.writeUint32LE(0); + out.write(&pixels.front(), pixels.size()); - // Write pixel data to BMP - out.write(pixels, lineSize * height); - - // Free allocated memory - delete[] pixels; return true; +#endif } } // End of namespace OpenGL diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp index 75e2cf8ef7..e41148062f 100644 --- a/backends/graphics/openglsdl/openglsdl-graphics.cpp +++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp @@ -633,7 +633,11 @@ bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) { for (int n = 0;; n++) { SDL_RWops *file; +#ifdef USE_PNG + filename = Common::String::format("scummvm%05d.png", n); +#else filename = Common::String::format("scummvm%05d.bmp", n); +#endif file = SDL_RWFromFile((screenshotsPath + filename).c_str(), "r"); diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp index ccfcc94ab3..9594587d88 100644 --- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp +++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp @@ -42,6 +42,10 @@ #include "graphics/scaler/aspect.h" #include "graphics/surface.h" #include "gui/EventRecorder.h" +#ifdef USE_PNG +#include "common/file.h" +#include "image/png.h" +#endif static const OSystem::GraphicsMode s_supportedShaders[] = { {"NONE", "Normal (no shader)", 0}, @@ -1321,8 +1325,66 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() { bool SurfaceSdlGraphicsManager::saveScreenshot(const char *filename) { assert(_hwscreen != NULL); - Common::StackLock lock(_graphicsMutex); // Lock the mutex until this function ends + Common::StackLock lock(_graphicsMutex); +#ifdef USE_PNG + Common::DumpFile out; + if (!out.open(filename)) { + return false; + } + +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Surface *rgbScreen = SDL_ConvertSurfaceFormat(_hwscreen, SDL_PIXELFORMAT_RGB24, 0); +#else + // This block of code was taken mostly as-is from SDL 1.2's SDL_SaveBMP_RW + SDL_Surface *rgbScreen = SDL_CreateRGBSurface(SDL_SWSURFACE, + _hwscreen->w, + _hwscreen->h, + 24, +#ifdef SCUMM_LITTLE_ENDIAN + 0x0000FF, 0x00FF00, 0xFF0000, +#else + 0xFF0000, 0x00FF00, 0x0000FF, +#endif + 0); + if (rgbScreen == nullptr) { + warning("Could not create RGB24 surface"); + return false; + } + + SDL_Rect bounds; + bounds.x = bounds.y = 0; + bounds.w = _hwscreen->w; + bounds.h = _hwscreen->h; + if (SDL_LowerBlit(_hwscreen, &bounds, rgbScreen, &bounds) < 0) { + SDL_FreeSurface(rgbScreen); + rgbScreen = nullptr; + } +#endif + + if (rgbScreen == nullptr) { + warning("Could not convert hardware surface to RGB24"); + return false; + } + + int result = SDL_LockSurface(rgbScreen); + if (result < 0) { + warning("Could not lock RGB surface"); + SDL_FreeSurface(rgbScreen); + return false; + } + + const Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0); + Graphics::Surface data; + data.init(rgbScreen->w, rgbScreen->h, rgbScreen->pitch, rgbScreen->pixels, format); + const bool success = Image::writePNG(out, data); + + SDL_UnlockSurface(rgbScreen); + SDL_FreeSurface(rgbScreen); + + return success; +#else return SDL_SaveBMP(_hwscreen, filename) == 0; +#endif } void SurfaceSdlGraphicsManager::setFullscreenMode(bool enable) { @@ -2534,7 +2596,11 @@ bool SurfaceSdlGraphicsManager::notifyEvent(const Common::Event &event) { for (int n = 0;; n++) { SDL_RWops *file; +#ifdef USE_PNG + filename = Common::String::format("scummvm%05d.png", n); +#else filename = Common::String::format("scummvm%05d.bmp", n); +#endif file = SDL_RWFromFile((screenshotsPath + filename).c_str(), "r"); -- cgit v1.2.3