mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Take screenshots with savestates.
This commit is contained in:
parent
b1c9e7382f
commit
2dd9b10c8d
@ -1317,6 +1317,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/Reporting.h
|
||||
Core/SaveState.cpp
|
||||
Core/SaveState.h
|
||||
Core/Screenshot.cpp
|
||||
Core/Screenshot.h
|
||||
Core/System.cpp
|
||||
Core/System.h
|
||||
Core/Util/GameManager.cpp
|
||||
|
@ -70,7 +70,7 @@
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib;../native/ext</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
@ -90,7 +90,7 @@
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../native;../native/ext/glew;../ext/zlib;../native/ext</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
@ -113,7 +113,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib;../native/ext</AdditionalIncludeDirectories>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
@ -143,7 +143,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../native;../native/ext/glew;../ext/zlib;../native/ext</AdditionalIncludeDirectories>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
@ -372,6 +372,7 @@
|
||||
<ClCompile Include="Reporting.cpp" />
|
||||
<ClCompile Include="SaveState.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSStackWalk.cpp" />
|
||||
<ClCompile Include="Screenshot.cpp" />
|
||||
<ClCompile Include="System.cpp" />
|
||||
<ClCompile Include="Util\BlockAllocator.cpp" />
|
||||
<ClCompile Include="Util\GameManager.cpp" />
|
||||
@ -563,6 +564,7 @@
|
||||
<ClInclude Include="Reporting.h" />
|
||||
<ClInclude Include="SaveState.h" />
|
||||
<ClInclude Include="MIPS\MIPSStackWalk.h" />
|
||||
<ClInclude Include="Screenshot.h" />
|
||||
<ClInclude Include="System.h" />
|
||||
<ClInclude Include="ThreadEventQueue.h" />
|
||||
<ClInclude Include="Util\BlockAllocator.h" />
|
||||
|
@ -529,6 +529,7 @@
|
||||
<ClCompile Include="HLE\sceSha256.cpp">
|
||||
<Filter>HLE\Libraries</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Screenshot.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@ -999,6 +1000,7 @@
|
||||
<ClInclude Include="HLE\sceSha256.h">
|
||||
<Filter>HLE\Libraries</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Screenshot.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/Screenshot.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/FileSystems/MetaFileSystem.h"
|
||||
#include "Core/ELF/ParamSFO.h"
|
||||
@ -55,6 +56,7 @@ namespace SaveState
|
||||
SAVESTATE_LOAD,
|
||||
SAVESTATE_VERIFY,
|
||||
SAVESTATE_REWIND,
|
||||
SAVESTATE_SAVE_SCREENSHOT,
|
||||
};
|
||||
|
||||
struct Operation
|
||||
@ -276,6 +278,11 @@ namespace SaveState
|
||||
Enqueue(Operation(SAVESTATE_REWIND, std::string(""), callback, cbUserData));
|
||||
}
|
||||
|
||||
void SaveScreenshot(const std::string &filename, Callback callback, void *cbUserData)
|
||||
{
|
||||
Enqueue(Operation(SAVESTATE_SAVE_SCREENSHOT, filename, callback, cbUserData));
|
||||
}
|
||||
|
||||
bool CanRewind()
|
||||
{
|
||||
return !rewindStates.Empty();
|
||||
@ -326,6 +333,7 @@ namespace SaveState
|
||||
void SaveSlot(int slot, Callback callback, void *cbUserData)
|
||||
{
|
||||
std::string fn = GenerateSaveSlotFilename(slot, STATE_EXTENSION);
|
||||
std::string shot = GenerateSaveSlotFilename(slot, SCREENSHOT_EXTENSION);
|
||||
if (!fn.empty()) {
|
||||
auto renameCallback = [=](bool status, void *data) {
|
||||
if (status) {
|
||||
@ -338,6 +346,8 @@ namespace SaveState
|
||||
callback(status, data);
|
||||
}
|
||||
};
|
||||
// Let's also create a screenshot.
|
||||
SaveScreenshot(shot, nullptr, 0);
|
||||
Save(fn + ".tmp", renameCallback, cbUserData);
|
||||
} else {
|
||||
I18NCategory *s = GetI18NCategory("Screen");
|
||||
@ -552,6 +562,10 @@ namespace SaveState
|
||||
}
|
||||
break;
|
||||
|
||||
case SAVESTATE_SAVE_SCREENSHOT:
|
||||
TakeGameScreenshot(op.filename.c_str(), SCREENSHOT_JPG, SCREENSHOT_RENDER);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(COMMON, "Savestate failure: unknown operation type %d", op.type);
|
||||
callbackResult = false;
|
||||
|
119
Core/Screenshot.cpp
Normal file
119
Core/Screenshot.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#ifdef USING_QT_UI
|
||||
#include <QtGui/QImage>
|
||||
#else
|
||||
#include <libpng17/png.h>
|
||||
#endif
|
||||
#include "ext/jpge/jpge.h"
|
||||
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Screenshot.h"
|
||||
#include "GPU/Common/GPUDebugInterface.h"
|
||||
#ifdef _WIN32
|
||||
#include "GPU/Directx9/GPU_DX9.h"
|
||||
#endif
|
||||
#include "GPU/GLES/GLES_GPU.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/GPUState.h"
|
||||
|
||||
bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type) {
|
||||
GPUDebugBuffer buf;
|
||||
bool success = false;
|
||||
|
||||
if (type == SCREENSHOT_RENDER) {
|
||||
if (gpuDebug) {
|
||||
success = gpuDebug->GetCurrentFramebuffer(buf);
|
||||
}
|
||||
} else {
|
||||
if (g_Config.iGPUBackend == GPU_BACKEND_OPENGL) {
|
||||
success = GLES_GPU::GetDisplayFramebuffer(buf);
|
||||
} else if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
|
||||
success = DX9::DIRECTX9_GPU::GetDisplayFramebuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USING_QT_UI
|
||||
if (success) {
|
||||
// TODO: Handle other formats (e.g. Direct3D.) Would only happen on Qt/Windows.
|
||||
const u8 *buffer = buf.GetData();
|
||||
QImage image(buffer, buf.GetStride(), buf.GetHeight(), QImage::Format_RGB888);
|
||||
image = image.mirrored();
|
||||
success = image.save(filename, fmt == SCREENSHOT_PNG ? "PNG" : "JPG");
|
||||
}
|
||||
#else
|
||||
if (success) {
|
||||
const u8 *buffer = buf.GetData();
|
||||
u8 *flipbuffer = nullptr;
|
||||
if (buf.GetFlipped()) {
|
||||
// Silly OpenGL reads upside down, we flip to another buffer for simplicity.
|
||||
flipbuffer = new u8[3 * buf.GetStride() * buf.GetHeight()];
|
||||
if (buf.GetFormat() == GPU_DBG_FORMAT_888_RGB) {
|
||||
for (u32 y = 0; y < buf.GetHeight(); y++) {
|
||||
memcpy(flipbuffer + y * buf.GetStride() * 3, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * 3, buf.GetStride() * 3);
|
||||
}
|
||||
} else if (buf.GetFormat() == GPU_DBG_FORMAT_8888) {
|
||||
for (u32 y = 0; y < buf.GetHeight(); y++) {
|
||||
for (u32 x = 0; x < buf.GetStride(); x++) {
|
||||
u8 *dst = &flipbuffer[(buf.GetHeight() - y - 1) * buf.GetStride() * 3 + x * 3];
|
||||
const u8 *src = &buffer[y * buf.GetStride() * 4 + x * 4];
|
||||
memcpy(dst, src, 3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
buffer = flipbuffer;
|
||||
} else if (buf.GetFormat() == GPU_DBG_FORMAT_8888_BGRA) {
|
||||
// Yay, we need to swap AND remove alpha.
|
||||
flipbuffer = new u8[3 * buf.GetStride() * buf.GetHeight()];
|
||||
for (u32 y = 0; y < buf.GetHeight(); y++) {
|
||||
for (u32 x = 0; x < buf.GetStride(); x++) {
|
||||
u8 *dst = &flipbuffer[y * buf.GetStride() * 3 + x * 3];
|
||||
const u8 *src = &buffer[y * buf.GetStride() * 4 + x * 4];
|
||||
dst[0] = src[2];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[0];
|
||||
}
|
||||
}
|
||||
buffer = flipbuffer;
|
||||
}
|
||||
|
||||
if (success && fmt == SCREENSHOT_PNG) {
|
||||
png_image png;
|
||||
memset(&png, 0, sizeof(png));
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.format = PNG_FORMAT_RGB;
|
||||
png.width = buf.GetStride();
|
||||
png.height = buf.GetHeight();
|
||||
png_image_write_to_file(&png, filename, 0, flipbuffer, buf.GetStride() * 3, NULL);
|
||||
png_image_free(&png);
|
||||
|
||||
success = png.warning_or_error < 2;
|
||||
} else if (success && fmt == SCREENSHOT_JPG) {
|
||||
jpge::params params;
|
||||
params.m_quality = 90;
|
||||
success = compress_image_to_jpeg_file(filename, buf.GetStride(), buf.GetHeight(), 3, flipbuffer, params);
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
delete [] flipbuffer;
|
||||
}
|
||||
#endif
|
||||
return success;
|
||||
}
|
33
Core/Screenshot.h
Normal file
33
Core/Screenshot.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
enum ScreenshotFormat {
|
||||
SCREENSHOT_PNG,
|
||||
SCREENSHOT_JPG,
|
||||
};
|
||||
|
||||
enum ScreenshotType {
|
||||
// What's begin show on screen (e.g. including FPS, etc.)
|
||||
SCREENSHOT_DISPLAY,
|
||||
// What the game rendered (e.g. at render resolution.)
|
||||
// Can only be used while in game.
|
||||
SCREENSHOT_RENDER,
|
||||
};
|
||||
|
||||
bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type);
|
@ -35,8 +35,6 @@
|
||||
#include <memory>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <libpng17/png.h>
|
||||
#include "ext/jpge/jpge.h"
|
||||
#include "Windows/DSoundStream.h"
|
||||
#include "Windows/WndMainWindow.h"
|
||||
#include "Windows/D3D9Base.h"
|
||||
@ -50,9 +48,8 @@
|
||||
#include "file/zip_read.h"
|
||||
#include "thread/thread.h"
|
||||
#include "net/http_client.h"
|
||||
#include "gfx_es2/gl_state.h" // only for screenshot!
|
||||
#include "gfx_es2/gl_state.h" // should've been only for screenshot - but actually not, cleanup?
|
||||
#include "gfx_es2/draw_text.h"
|
||||
#include "gfx_es2/draw_buffer.h"
|
||||
#include "gfx/gl_lost_manager.h"
|
||||
#include "gfx/texture.h"
|
||||
#include "i18n/i18n.h"
|
||||
@ -76,13 +73,10 @@
|
||||
#include "Core/Host.h"
|
||||
#include "Core/PSPMixer.h"
|
||||
#include "Core/SaveState.h"
|
||||
#include "Core/Screenshot.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/HLE/sceCtrl.h"
|
||||
#include "Core/Util/GameManager.h"
|
||||
#include "GPU/GLES/GLES_GPU.h"
|
||||
#ifdef _WIN32
|
||||
#include "GPU/Directx9/GPU_DX9.h"
|
||||
#endif
|
||||
|
||||
#include "ui_atlas.h"
|
||||
#include "EmuScreen.h"
|
||||
@ -604,7 +598,7 @@ void NativeShutdownGraphics() {
|
||||
void TakeScreenshot() {
|
||||
g_TakeScreenshot = false;
|
||||
|
||||
#if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE))
|
||||
#if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE))
|
||||
mkDir(g_Config.memStickDirectory + "/PSP/SCREENSHOT");
|
||||
|
||||
// First, find a free filename.
|
||||
@ -627,73 +621,7 @@ void TakeScreenshot() {
|
||||
i++;
|
||||
}
|
||||
|
||||
GPUDebugBuffer buf;
|
||||
bool success = true;
|
||||
|
||||
if (g_Config.iGPUBackend == GPU_BACKEND_OPENGL) {
|
||||
success = GLES_GPU::GetDisplayFramebuffer(buf);
|
||||
} else if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
|
||||
success = DX9::DIRECTX9_GPU::GetDisplayFramebuffer(buf);
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
#ifdef USING_QT_UI
|
||||
if (success) {
|
||||
// TODO: Handle other formats (e.g. Direct3D.) Would only happen on Qt/Windows.
|
||||
const u8 *buffer = buf.GetData();
|
||||
QImage image(buffer, buf.GetStride(), buf.GetHeight(), QImage::Format_RGB888);
|
||||
image = image.mirrored();
|
||||
success = image.save(filename, g_Config.bScreenshotsAsPNG ? "PNG" : "JPG");
|
||||
}
|
||||
#else
|
||||
if (success) {
|
||||
const u8 *buffer = buf.GetData();
|
||||
u8 *flipbuffer = nullptr;
|
||||
if (buf.GetFlipped()) {
|
||||
// Silly OpenGL reads upside down, we flip to another buffer for simplicity.
|
||||
_assert_msg_(G3D, buf.GetFormat() == GPU_DBG_FORMAT_888_RGB, "Expecting RGB for flipped buffer (OpenGL.)");
|
||||
flipbuffer = new u8[3 * buf.GetStride() * buf.GetHeight()];
|
||||
for (u32 y = 0; y < buf.GetHeight(); y++) {
|
||||
memcpy(flipbuffer + y * buf.GetStride() * 3, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * 3, buf.GetStride() * 3);
|
||||
}
|
||||
buffer = flipbuffer;
|
||||
}
|
||||
if (buf.GetFormat() == GPU_DBG_FORMAT_8888_BGRA) {
|
||||
// Yay, we need to swap AND remove alpha.
|
||||
flipbuffer = new u8[3 * buf.GetStride() * buf.GetHeight()];
|
||||
for (u32 y = 0; y < buf.GetHeight(); y++) {
|
||||
for (u32 x = 0; x < buf.GetStride(); x++) {
|
||||
u8 *dst = &flipbuffer[y * buf.GetStride() * 3 + x * 3];
|
||||
const u8 *src = &buffer[y * buf.GetStride() * 4 + x * 4];
|
||||
dst[0] = src[2];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[0];
|
||||
}
|
||||
}
|
||||
buffer = flipbuffer;
|
||||
}
|
||||
|
||||
if (g_Config.bScreenshotsAsPNG) {
|
||||
png_image png;
|
||||
memset(&png, 0, sizeof(png));
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.format = PNG_FORMAT_RGB;
|
||||
png.width = buf.GetStride();
|
||||
png.height = buf.GetHeight();
|
||||
png_image_write_to_file(&png, filename, 0, flipbuffer, buf.GetStride() * 3, NULL);
|
||||
png_image_free(&png);
|
||||
|
||||
success = png.warning_or_error >= 2;
|
||||
} else {
|
||||
jpge::params params;
|
||||
params.m_quality = 90;
|
||||
success = compress_image_to_jpeg_file(filename, buf.GetStride(), buf.GetHeight(), 3, flipbuffer, params);
|
||||
}
|
||||
delete [] flipbuffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool success = TakeGameScreenshot(filename, g_Config.bScreenshotsAsPNG ? SCREENSHOT_PNG : SCREENSHOT_JPG, SCREENSHOT_DISPLAY);
|
||||
if (success) {
|
||||
osm.Show(filename);
|
||||
} else {
|
||||
|
@ -199,6 +199,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/Core/MemMapFunctions.cpp \
|
||||
$(SRC)/Core/Reporting.cpp \
|
||||
$(SRC)/Core/SaveState.cpp \
|
||||
$(SRC)/Core/Screenshot.cpp \
|
||||
$(SRC)/Core/System.cpp \
|
||||
$(SRC)/Core/PSPMixer.cpp \
|
||||
$(SRC)/Core/Debugger/Breakpoints.cpp \
|
||||
|
Loading…
Reference in New Issue
Block a user