SDL: Implement screenshot saving

This commit is contained in:
DouglasLiuGamer 2019-02-12 14:43:18 +08:00 committed by Bastien Bouclet
parent 203577d66b
commit d4d6e6203b
10 changed files with 185 additions and 0 deletions

View file

@ -78,6 +78,8 @@ public:
virtual Graphics::PixelBuffer getScreenPixelBuffer() = 0;
// ResidualVM specific method
virtual void suggestSideTextures(Graphics::Surface *left, Graphics::Surface *right) = 0;
// ResidualVM specific method
virtual void saveScreenshot() {}
virtual int16 getHeight() const = 0;
virtual int16 getWidth() const = 0;

View file

@ -28,6 +28,7 @@
#include "backends/events/sdl/sdl-events.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "engines/engine.h"
#include "graphics/pixelbuffer.h"
#include "graphics/opengl/context.h"
@ -36,6 +37,7 @@
#include "graphics/opengl/system_headers.h"
#include "graphics/opengl/texture.h"
#include "graphics/opengl/tiledsurface.h"
#include "image/png.h"
OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window, const Capabilities &capabilities)
:
@ -652,4 +654,64 @@ void OpenGLSdlGraphicsManager::deinitializeRenderer() {
}
#endif // SDL_VERSION_ATLEAST(2, 0, 0)
bool OpenGLSdlGraphicsManager::saveScreenshot(const Common::String &file) const {
// Largely based on the implementation from ScummVM
uint width = _overlayScreen->getWidth();
uint height = _overlayScreen->getHeight();
uint linePaddingSize = width % 4;
uint lineSize = width * 3 + linePaddingSize;
Common::DumpFile out;
if (!out.open(file)) {
return false;
}
Common::Array<uint8> pixels;
pixels.resize(lineSize * height);
if (_frameBuffer) {
_frameBuffer->detach();
}
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels.front());
if (_frameBuffer) {
_frameBuffer->attach();
}
#ifdef USE_PNG
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
for (uint y = height; y-- > 0;) {
uint8 *line = &pixels.front() + y * lineSize;
for (uint x = width; x > 0; --x, line += 3) {
SWAP(line[0], line[2]);
}
}
out.writeByte('B');
out.writeByte('M');
out.writeUint32LE(height * lineSize + 54);
out.writeUint32LE(0);
out.writeUint32LE(54);
out.writeUint32LE(40);
out.writeUint32LE(width);
out.writeUint32LE(height);
out.writeUint16LE(1);
out.writeUint16LE(24);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.writeUint32LE(0);
out.write(&pixels.front(), pixels.size());
return true;
#endif
}
#endif

View file

@ -112,6 +112,9 @@ protected:
Math::Rect2d computeGameRect(bool renderToFrameBuffer, uint gameWidth, uint gameHeight,
uint screenWidth, uint screenHeight);
// ResVmSdlGraphicsManager API
bool saveScreenshot(const Common::String &file) const override;
int _antialiasing;
bool _vsync;

View file

@ -24,9 +24,11 @@
#include "backends/platform/sdl/sdl-sys.h"
#include "backends/events/sdl/sdl-events.h"
#include "backends/platform/sdl/sdl.h"
#include "common/config-manager.h"
#include "common/textconsole.h"
#include "common/file.h"
static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
{0, 0, 0}
@ -239,6 +241,10 @@ bool ResVmSdlGraphicsManager::notifyEvent(const Common::Event &event) {
//ResidualVM specific:
switch ((int)event.type) {
case Common::EVENT_KEYDOWN:
if (event.kbd.hasFlags(Common::KBD_ALT) && event.kbd.keycode == Common::KEYCODE_s) {
saveScreenshot();
return true;
}
break;
case Common::EVENT_KEYUP:
break;
@ -260,3 +266,41 @@ bool ResVmSdlGraphicsManager::notifyMousePosition(Common::Point mouse) {
//setMousePos(mouse.x, mouse.y);
return true;
}
void ResVmSdlGraphicsManager::saveScreenshot() {
OSystem_SDL *g_systemSdl = dynamic_cast<OSystem_SDL*>(g_system);
if (g_systemSdl) {
Common::String filename;
Common::String path = g_systemSdl->getScreenshotsPath();
// Find unused filename
int n = 0;
while (true) {
#ifdef USE_PNG
filename = Common::String::format("residualvm%05d.png", n);
#else
filename = Common::String::format("residualvm%05d.bmp", n);
#endif
SDL_RWops *file = SDL_RWFromFile((path + filename).c_str(), "r");
if (!file) {
break;
}
SDL_RWclose(file);
++n;
}
if (saveScreenshot(path + filename)) {
if (path.empty())
debug("Saved screenshot '%s' in current directory", filename.c_str());
else
debug("Saved screenshot '%s' in directory '%s'", filename.c_str(), path.c_str());
} else {
if (path.empty())
warning("Could not save screenshot in current directory");
else
warning("Could not save screenshot in directory '%s'", path.c_str());
}
}
}

View file

@ -99,6 +99,7 @@ public:
virtual void unlockScreen() override;
virtual void fillScreen(uint32 col) override;
virtual void setShakePos(int shakeOffset) override;
void saveScreenshot() override;
// GraphicsManager API - Focus Rectangle
virtual void setFocusRectangle(const Common::Rect& rect) override;
@ -145,6 +146,9 @@ protected:
/** Obtain the user configured fullscreen resolution, or default to the desktop resolution */
Common::Rect getPreferredFullscreenResolution();
/** Save a screenshot to the specified file */
virtual bool saveScreenshot(const Common::String &file) const = 0;
};
#endif

View file

@ -27,9 +27,11 @@
#include "backends/graphics/surfacesdl/surfacesdl-graphics.h"
#include "backends/events/sdl/sdl-events.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "engines/engine.h"
#include "graphics/pixelbuffer.h"
#include "graphics/surface.h"
#include "image/png.h"
SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSource, SdlWindow *window, const Capabilities &capabilities)
:
@ -468,4 +470,57 @@ SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height,
}
#endif // SDL_VERSION_ATLEAST(2, 0, 0)
bool SurfaceSdlGraphicsManager::saveScreenshot(const Common::String &file) const {
// Based on the implementation from ScummVM
bool success;
SDL_Surface *screen = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 0)
int width, height;
SDL_GetRendererOutputSize(_renderer, &width, &height);
screen = SDL_CreateRGBSurface(SDL_SWSURFACE,
width,
height,
24,
#ifdef SCUMM_LITTLE_ENDIAN
0x0000FF, 0x00FF00, 0xFF0000,
#else
0xFF0000, 0x00FF00, 0x0000FF,
#endif // SCUMM_LITTLE_ENDIAN
0);
SDL_RenderReadPixels(_renderer, nullptr, SDL_PIXELFORMAT_RGB24, screen->pixels, screen->pitch);
#else
screen = _screen;
#endif // SDL_VERSION_ATLEAST(2, 0, 0)
#ifdef USE_PNG
Common::DumpFile out;
if (!out.open(file)) {
success = false;
} else {
if (SDL_LockSurface(screen) < 0) {
warning("Could not lock the screen surface");
success = false;
}
Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0);
Graphics::Surface data;
data.init(width, height, screen->pitch, screen->pixels, format);
success = Image::writePNG(out, data);
SDL_UnlockSurface(screen);
}
#else
success = SDL_SaveBMP(screen, file.c_str()) == 0;
#endif
if (screen && screen != _screen) {
SDL_FreeSurface(screen);
}
return success;
}
#endif

View file

@ -91,6 +91,9 @@ protected:
void drawOverlay();
void drawSideTextures();
void closeOverlay();
// ResVmSdlGraphicsManager API
virtual bool saveScreenshot(const Common::String &file) const override;
};
#endif

View file

@ -266,6 +266,10 @@ void ModularBackend::setCursorPalette(const byte *colors, uint start, uint num)
_graphicsManager->setCursorPalette(colors, start, num);
}
void ModularBackend::saveScreenshot() {
_graphicsManager->saveScreenshot();
}
OSystem::MutexRef ModularBackend::createMutex() {
assert(_mutexManager);
return _mutexManager->createMutex();

View file

@ -84,6 +84,7 @@ public:
virtual void suggestSideTextures(Graphics::Surface *left, Graphics::Surface *right); // ResidualVM specific method
virtual void initSizeHint(const Graphics::ModeList &modes) override;
virtual int getScreenChangeID() const override;
virtual void saveScreenshot() override; // ResidualVM specific method
virtual void beginGFXTransaction() override;
virtual OSystem::TransactionError endGFXTransaction() override;

View file

@ -1041,6 +1041,13 @@ public:
*/
virtual void clearFocusRectangle() {}
/**
* !!! ResidualVM specific method !!!
* Instruct the backend to capture a screenshot of the current screen.
*
* The backend can persist it the way it considers appropriate.
*/
virtual void saveScreenshot() {}
//@}