mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-05-01 14:17:58 +03:00
glrage: make .jpg screenshots
This commit is contained in:
parent
6402a325d6
commit
b3d5703b92
18 changed files with 90 additions and 126 deletions
|
@ -221,7 +221,6 @@ sources = [
|
|||
'src/glrage/ContextImpl.cpp',
|
||||
'src/glrage/GLRage.cpp',
|
||||
'src/glrage/Interop.cpp',
|
||||
'src/glrage/Screenshot.cpp',
|
||||
'src/glrage_gl/Buffer.cpp',
|
||||
'src/glrage_gl/Program.cpp',
|
||||
'src/glrage_gl/Sampler.cpp',
|
||||
|
|
|
@ -83,7 +83,7 @@ HRESULT DirectDrawSurface::Blt(
|
|||
|
||||
gl::Screenshot::capture(
|
||||
buffer, width, height, depth, GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV);
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV, false);
|
||||
|
||||
Blitter::Rect srcRect { 0, height, width, 0 };
|
||||
|
||||
|
|
|
@ -856,3 +856,8 @@ void Output_ApplyWaterEffect(float *r, float *g, float *b)
|
|||
*b *= m_WaterColor.b;
|
||||
}
|
||||
}
|
||||
|
||||
bool Output_MakeScreenshot(const char *path)
|
||||
{
|
||||
return S_Output_MakeScreenshot(path);
|
||||
}
|
||||
|
|
|
@ -71,4 +71,6 @@ void Output_AnimateTextures(int32_t ticks);
|
|||
|
||||
void Output_ApplyWaterEffect(float *r, float *g, float *b);
|
||||
|
||||
bool Output_MakeScreenshot(const char *path);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "3dsystem/phd_math.h"
|
||||
#include "config.h"
|
||||
#include "filesystem.h"
|
||||
#include "game/clock.h"
|
||||
#include "game/demo.h"
|
||||
#include "game/fmv.h"
|
||||
|
@ -186,3 +187,16 @@ void Shell_Wait(int nticks)
|
|||
Input_Update();
|
||||
}
|
||||
}
|
||||
|
||||
bool Shell_MakeScreenshot()
|
||||
{
|
||||
char path[20];
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
sprintf(path, "screenshot%04d.jpg", i);
|
||||
if (File_Exists(path)) {
|
||||
continue;
|
||||
}
|
||||
return Output_MakeScreenshot(path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#ifndef T1M_GAME_SHELL_H
|
||||
#define T1M_GAME_SHELL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void Shell_Main();
|
||||
void Shell_ExitSystem(const char *message);
|
||||
void Shell_ExitSystemFmt(const char *fmt, ...);
|
||||
void Shell_Wait(int nticks);
|
||||
bool Shell_MakeScreenshot();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
virtual bool isRendered() = 0;
|
||||
virtual HWND getHWnd() = 0;
|
||||
virtual std::string getBasePath() = 0;
|
||||
virtual void scheduleScreenshot(const std::string &path) = 0;
|
||||
|
||||
virtual ~Context() = default;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "glrage/ContextImpl.hpp"
|
||||
|
||||
#include "glrage_gl/Screenshot.hpp"
|
||||
#include "glrage_gl/gl_core_3_3.h"
|
||||
#include "glrage_gl/wgl_ext.h"
|
||||
#include "glrage_util/ErrorUtils.hpp"
|
||||
|
@ -109,17 +110,6 @@ LRESULT
|
|||
ContextImpl::windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg) {
|
||||
// Printscreen on Windows with OpenGL doesn't work in fullscreen, so
|
||||
// hook the key and implement screenshot saving to files.
|
||||
// For some reason, VK_SNAPSHOT doesn't generate WM_KEYDOWN events but
|
||||
// only WM_KEYUP. Works just as well, though.
|
||||
case WM_KEYUP:
|
||||
if (wParam == VK_SNAPSHOT) {
|
||||
m_screenshot.schedule(true);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
// force default handling for some window messages when in windowed
|
||||
// mode, especially important for Tomb Raider
|
||||
case WM_MOVE:
|
||||
|
@ -223,11 +213,9 @@ void ContextImpl::swapBuffers()
|
|||
{
|
||||
glFinish();
|
||||
|
||||
try {
|
||||
m_screenshot.captureScheduled();
|
||||
} catch (const std::exception &ex) {
|
||||
ErrorUtils::warning("Can't capture screenshot", ex);
|
||||
m_screenshot.schedule(false);
|
||||
if (!m_screenshotScheduledPath.empty()) {
|
||||
gl::Screenshot::capture(m_screenshotScheduledPath);
|
||||
m_screenshotScheduledPath.clear();
|
||||
}
|
||||
|
||||
SwapBuffers(m_hdc);
|
||||
|
@ -270,4 +258,9 @@ std::string ContextImpl::getBasePath()
|
|||
return path;
|
||||
}
|
||||
|
||||
void ContextImpl::scheduleScreenshot(const std::string &path)
|
||||
{
|
||||
m_screenshotScheduledPath = path;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "glrage/Context.hpp"
|
||||
#include "glrage/Screenshot.hpp"
|
||||
|
||||
namespace glrage {
|
||||
|
||||
|
@ -30,6 +29,7 @@ public:
|
|||
bool isRendered();
|
||||
HWND getHWnd();
|
||||
std::string getBasePath();
|
||||
void scheduleScreenshot(const std::string &path);
|
||||
|
||||
private:
|
||||
ContextImpl();
|
||||
|
@ -66,9 +66,6 @@ private:
|
|||
// rendering flag
|
||||
bool m_render = false;
|
||||
|
||||
// screenshot object
|
||||
Screenshot m_screenshot;
|
||||
|
||||
// DirectDraw display mode
|
||||
int32_t m_displayWidth = 0;
|
||||
int32_t m_displayHeight = 0;
|
||||
|
@ -80,6 +77,9 @@ private:
|
|||
// Window size, controlled by SDL
|
||||
int32_t m_windowWidth = 0;
|
||||
int32_t m_windowHeight = 0;
|
||||
|
||||
// whether to capture screenshot on next redraw
|
||||
std::string m_screenshotScheduledPath;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -27,3 +27,10 @@ void GLRage_SetWindowSize(int width, int height)
|
|||
auto &context = GLRage::getContext();
|
||||
context.setWindowSize(width, height);
|
||||
}
|
||||
|
||||
bool GLRage_MakeScreenshot(const char *path)
|
||||
{
|
||||
auto &context = GLRage::getContext();
|
||||
context.scheduleScreenshot(std::string(path));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ void GLRage_Detach();
|
|||
void GLRage_SetFullscreen(bool fullscreen);
|
||||
void GLRage_SetWindowSize(int width, int height);
|
||||
|
||||
bool GLRage_MakeScreenshot(const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
#include "glrage/Screenshot.hpp"
|
||||
|
||||
#include "glrage/ContextImpl.hpp"
|
||||
#include "glrage_gl/Screenshot.hpp"
|
||||
#include "glrage_util/StringUtils.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace glrage {
|
||||
|
||||
void Screenshot::schedule(bool schedule)
|
||||
{
|
||||
m_schedule = schedule;
|
||||
}
|
||||
|
||||
void Screenshot::captureScheduled()
|
||||
{
|
||||
if (m_schedule) {
|
||||
capture();
|
||||
m_schedule = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Screenshot::capture()
|
||||
{
|
||||
// find unused screenshot file name
|
||||
std::string basePath = ContextImpl::instance().getBasePath();
|
||||
std::string path;
|
||||
DWORD dwAttrib;
|
||||
do {
|
||||
std::string fileName =
|
||||
StringUtils::format("screenshot%04d.tga", m_index++);
|
||||
path = basePath + "\\" + fileName;
|
||||
dwAttrib = GetFileAttributes(path.c_str());
|
||||
} while (dwAttrib != INVALID_FILE_ATTRIBUTES && m_index < 9999);
|
||||
|
||||
// rather unlikely, but better safe than sorry
|
||||
if (dwAttrib != INVALID_FILE_ATTRIBUTES) {
|
||||
throw std::runtime_error("All available screenshot slots are used up!");
|
||||
}
|
||||
|
||||
// actual capture
|
||||
gl::Screenshot::capture(path);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace glrage {
|
||||
|
||||
class Screenshot {
|
||||
public:
|
||||
void schedule(bool schedule);
|
||||
void captureScheduled();
|
||||
void capture();
|
||||
|
||||
private:
|
||||
uint32_t m_index = 0;
|
||||
bool m_schedule = false;
|
||||
};
|
||||
|
||||
}
|
|
@ -3,6 +3,11 @@
|
|||
#include "glrage_util/ErrorUtils.hpp"
|
||||
#include "glrage_util/StringUtils.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "memory.h"
|
||||
#include "game/picture.h"
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
|
@ -14,46 +19,34 @@
|
|||
namespace glrage {
|
||||
namespace gl {
|
||||
|
||||
// heavily simplified Targa header struct for raw BGR(A) data
|
||||
struct TGAHeader {
|
||||
uint8_t blank1[2];
|
||||
uint8_t format;
|
||||
uint8_t blank2[9];
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t depth;
|
||||
uint8_t blank3;
|
||||
};
|
||||
|
||||
void Screenshot::capture(const std::string &path)
|
||||
bool Screenshot::capture(const std::string &path)
|
||||
{
|
||||
// open screenshot file
|
||||
std::ofstream file(path, std::ofstream::binary);
|
||||
if (!file.good()) {
|
||||
throw std::runtime_error(
|
||||
"Can't open screenshot file '" + path
|
||||
+ "': " + ErrorUtils::getSystemErrorString());
|
||||
}
|
||||
bool ret = false;
|
||||
|
||||
// copy framebuffer to local buffer
|
||||
GLint width;
|
||||
GLint height;
|
||||
GLint depth = 3;
|
||||
|
||||
std::vector<uint8_t> buffer;
|
||||
capture(buffer, width, height, depth, GL_BGR, GL_UNSIGNED_BYTE, false);
|
||||
|
||||
// create Targa header
|
||||
TGAHeader tgaHeader = { { 0, 0 }, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 0,
|
||||
0, 0 };
|
||||
tgaHeader.format = 2;
|
||||
tgaHeader.width = width;
|
||||
tgaHeader.height = height;
|
||||
tgaHeader.depth = depth * 8;
|
||||
Screenshot::capture(
|
||||
buffer, width, height, 3, GL_RGB, GL_UNSIGNED_BYTE, true);
|
||||
|
||||
file.write(reinterpret_cast<char *>(&tgaHeader), sizeof(TGAHeader));
|
||||
file.write(reinterpret_cast<char *>(&buffer[0]), buffer.size());
|
||||
file.close();
|
||||
PICTURE *pic = Picture_Create();
|
||||
if (!pic) {
|
||||
goto cleanup;
|
||||
}
|
||||
pic->width = width;
|
||||
pic->height = height;
|
||||
pic->data = static_cast<RGB888 *>(Memory_Alloc(width * height * 3));
|
||||
std::copy(
|
||||
buffer.begin(), buffer.end(), reinterpret_cast<uint8_t *>(pic->data));
|
||||
|
||||
ret = Picture_SaveToFile(pic, path.c_str());
|
||||
|
||||
cleanup:
|
||||
if (pic) {
|
||||
Picture_Free(pic);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Screenshot::capture(
|
||||
|
|
|
@ -11,10 +11,10 @@ namespace gl {
|
|||
|
||||
class Screenshot {
|
||||
public:
|
||||
static void capture(const std::string &path);
|
||||
static bool capture(const std::string &path);
|
||||
static void capture(
|
||||
std::vector<uint8_t> &buffer, GLint &width, GLint &height, GLint depth,
|
||||
GLenum format, GLenum type, bool vflip = false);
|
||||
GLenum format, GLenum type, bool vflip);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1543,3 +1543,8 @@ void S_Output_DownloadTextures(int32_t pages)
|
|||
|
||||
m_SelectedTexture = -1;
|
||||
}
|
||||
|
||||
bool S_Output_MakeScreenshot(const char *path)
|
||||
{
|
||||
return GLRage_MakeScreenshot(path);
|
||||
}
|
||||
|
|
|
@ -54,4 +54,6 @@ void S_Output_DrawLightningSegment(
|
|||
int x1, int y1, int z1, int thickness1, int x2, int y2, int z2,
|
||||
int thickness2);
|
||||
|
||||
bool S_Output_MakeScreenshot(const char *path);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -87,6 +87,13 @@ void S_Shell_SpinMessageLoop()
|
|||
break;
|
||||
}
|
||||
|
||||
case SDL_KEYUP:
|
||||
if (event.key.keysym.sym == SDLK_PRINTSCREEN) {
|
||||
Shell_MakeScreenshot();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue