[renderer] Merge screenshot code as much as possible

Only CaptureBGR is per-renderer as the rest of the screenshot code uses
it to do the actual capture (which is target dependent). Vulkan is
currently broken due to capture being an asynchronous process and the
rest of the code expecting capture to be synchronous (also, bgr vs rgb).

The best thing is all renderers now write the same format (currently
png).
This commit is contained in:
Bill Currie 2022-03-07 14:39:47 +09:00
parent 2eae2e5d74
commit ea2fd32228
13 changed files with 98 additions and 281 deletions

View file

@ -97,9 +97,6 @@ typedef struct vid_render_funcs_s {
void (*Draw_SubPic) (int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height);
struct tex_s *(*SCR_CaptureBGR) (void);
struct tex_s *(*SCR_ScreenShot) (unsigned width, unsigned height);
void (*SCR_DrawStringToSnap) (const char *s, struct tex_s *tex,
int x, int y);
void (*Fog_Update) (float density, float red, float green, float blue,
float time);
@ -112,9 +109,7 @@ typedef struct vid_render_funcs_s {
void (*R_LoadSkys) (const char *);
void (*R_NewMap) (model_t *worldmodel, model_t **models, int num_models);
void (*R_LineGraph) (int x, int y, int *h_vals, int count, int height);
void (*R_ViewChanged) (void);
void (*SCR_ScreenShot_f) (void);
vid_model_funcs_t *model_funcs;
} vid_render_funcs_t;

View file

@ -30,6 +30,7 @@
#define __QF_screen_h
struct transform_s;
struct tex_s;
void SCR_Init (void);
void SCR_SetFOV (float fov);
@ -42,6 +43,8 @@ typedef void (*SCR_Func)(void);
// scr_funcs is a null terminated array
void SCR_UpdateScreen (struct transform_s *camera, double realtime,
SCR_Func *scr_funcs);
void SCR_DrawStringToSnap (const char *s, struct tex_s *tex, int x, int y);
struct tex_s *SCR_SnapScreen (unsigned width, unsigned height);
extern struct cvar_s *hud_fps, *hud_time;

View file

@ -83,89 +83,6 @@ gl_SCR_CaptureBGR (void)
return tex;
}
tex_t *
gl_SCR_ScreenShot (unsigned width, unsigned height)
{
unsigned char *src, *dest, *snap;
float fracw, frach;
int count, dex, dey, dx, dy, nx, r, g, b, x, y, w, h;
tex_t *tex;
snap = Hunk_TempAlloc (0, vid.width * vid.height * 3);
qfglReadPixels (0, 0, vid.width, vid.height, GL_RGB, GL_UNSIGNED_BYTE,
snap);
w = (vid.width < width) ? vid.width : width;
h = (vid.height < height) ? vid.height : height;
fracw = (float) vid.width / (float) w;
frach = (float) vid.height / (float) h;
tex = malloc (sizeof (tex_t) + w * h);
tex->data = (byte *) (tex + 1);
if (!tex)
return 0;
tex->width = w;
tex->height = h;
tex->palette = vid.palette;
for (y = 0; y < h; y++) {
dest = tex->data + (w * y);
for (x = 0; x < w; x++) {
r = g = b = 0;
dx = x * fracw;
dex = (x + 1) * fracw;
if (dex == dx)
dex++; // at least one
dy = y * frach;
dey = (y + 1) * frach;
if (dey == dy)
dey++; // at least one
count = 0;
for (; dy < dey; dy++) {
src = snap + (vid.width * 3 * dy) + dx * 3;
for (nx = dx; nx < dex; nx++) {
r += *src++;
g += *src++;
b += *src++;
count++;
}
}
r /= count;
g /= count;
b /= count;
*dest++ = MipColor (r, g, b);
}
}
return tex;
}
void
gl_SCR_ScreenShot_f (void)
{
dstring_t *pcxname = dstring_new ();
// find a file name to save it to
if (!QFS_NextFilename (pcxname,
va (0, "%s/qf", qfs_gamedir->dir.shots), ".tga")) {
Sys_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
} else {
tex_t *tex;
tex = gl_SCR_CaptureBGR ();
WriteTGAfile (pcxname->str, tex->data, tex->width, tex->height);
free (tex);
Sys_Printf ("Wrote %s/%s\n", qfs_userpath, pcxname->str);
}
dstring_delete (pcxname);
}
static void
SCR_TileClear (void)
{

View file

@ -219,29 +219,3 @@ glsl_SCR_CaptureBGR (void)
}
return tex;
}
__attribute__((const)) tex_t *
glsl_SCR_ScreenShot (unsigned width, unsigned height)
{
return 0;
}
void
glsl_SCR_ScreenShot_f (void)
{
dstring_t *name = dstring_new ();
// find a file name to save it to
if (!QFS_NextFilename (name, va (0, "%s/qf",
qfs_gamedir->dir.shots), ".png")) {
Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PNG file\n");
} else {
tex_t *tex;
tex = glsl_SCR_CaptureBGR ();
WritePNGqfs (name->str, tex->data, tex->width, tex->height);
free (tex);
Sys_Printf ("Wrote %s/%s\n", qfs_userpath, name->str);
}
dstring_delete (name);
}

View file

@ -38,11 +38,15 @@
#include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/draw.h"
#include "QF/dstring.h"
#include "QF/image.h"
#include "QF/png.h"
#include "QF/pcx.h"
#include "QF/quakefs.h"
#include "QF/render.h"
#include "QF/screen.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "QF/scene/transform.h"
#include "QF/ui/view.h"
@ -185,7 +189,21 @@ SCR_SetFOV (float fov)
static void
ScreenShot_f (void)
{
r_funcs->SCR_ScreenShot_f ();
dstring_t *name = dstring_new ();
// find a file name to save it to
if (!QFS_NextFilename (name, va (0, "%s/qf",
qfs_gamedir->dir.shots), ".png")) {
Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PNG file\n");
} else {
tex_t *tex;
tex = r_funcs->SCR_CaptureBGR ();
WritePNGqfs (name->str, tex->data, tex->width, tex->height);
free (tex);
Sys_Printf ("Wrote %s/%s\n", qfs_userpath, name->str);
}
dstring_delete (name);
}
/*
@ -347,12 +365,74 @@ SCR_DrawStringToSnap (const char *s, tex_t *tex, int x, int y)
}
}
tex_t *
SCR_SnapScreen (unsigned width, unsigned height)
{
byte *src, *dest;
float fracw, frach;
unsigned count, dex, dey, dx, dy, nx, r, g, b, x, y, w, h;
tex_t *tex;
tex_t *snap = r_funcs->SCR_CaptureBGR ();
//FIXME casts
w = ((unsigned)snap->width < width) ? (unsigned)snap->width : width;
h = ((unsigned)snap->height < height) ? (unsigned)snap->height : height;
fracw = (float) snap->width / (float) w;
frach = (float) snap->height / (float) h;
tex = malloc (sizeof (tex_t) + w * h);
tex->data = (byte *) (tex + 1);
if (!tex)
return 0;
tex->width = w;
tex->height = h;
tex->palette = r_data->vid->palette;
for (y = 0; y < h; y++) {
dest = tex->data + (w * y);
for (x = 0; x < w; x++) {
r = g = b = 0;
dx = x * fracw;
dex = (x + 1) * fracw;
if (dex == dx)
dex++; // at least one
dy = y * frach;
dey = (y + 1) * frach;
if (dey == dy)
dey++; // at least one
count = 0;
for (; dy < dey; dy++) {
src = snap->data + (snap->width * 3 * dy) + dx * 3;
for (nx = dx; nx < dex; nx++) {
b += *src++;
g += *src++;
r += *src++;
count++;
}
}
r /= count;
g /= count;
b /= count;
*dest++ = MipColor (r, g, b);
}
}
free (snap);
return tex;
}
void
SCR_Init (void)
{
// register our commands
Cmd_AddCommand ("screenshot", ScreenShot_f, "Take a screenshot, "
"saves as qfxxx.pcx in the current directory");
"saves as qfxxxx.png in the QF directory");
Cmd_AddCommand ("sizeup", SCR_SizeUp_f, "Increases the screen size");
Cmd_AddCommand ("sizedown", SCR_SizeDown_f, "Decreases the screen size");

View file

@ -85,88 +85,6 @@ SCR_CaptureBGR (void)
return tex;
}
tex_t *
SCR_ScreenShot (unsigned width, unsigned height)
{
unsigned char *src, *dest;
float fracw, frach;
int count, dex, dey, dx, dy, nx, r, g, b, x, y, w, h;
tex_t *tex;
w = (vid.width < width) ? vid.width : width;
h = (vid.height < height) ? vid.height : height;
fracw = (float) vid.width / (float) w;
frach = (float) vid.height / (float) h;
tex = malloc (sizeof (tex_t) + w * h);
tex->data = (byte *) (tex + 1);
if (!tex)
return 0;
tex->width = w;
tex->height = h;
tex->palette = vid.palette;
for (y = 0; y < h; y++) {
dest = tex->data + (w * (h - y - 1));
for (x = 0; x < w; x++) {
r = g = b = 0;
dx = x * fracw;
dex = (x + 1) * fracw;
if (dex == dx)
dex++; // at least one
dy = y * frach;
dey = (y + 1) * frach;
if (dey == dy)
dey++; // at least one
count = 0;
for (; dy < dey; dy++) {
src = ((byte*)vid.buffer) + (vid.rowbytes * dy) + dx;
for (nx = dx; nx < dex; nx++) {
r += vid.basepal[*src * 3];
g += vid.basepal[*src * 3 + 1];
b += vid.basepal[*src * 3 + 2];
src++;
count++;
}
}
r /= count;
g /= count;
b /= count;
*dest++ = MipColor (r, g, b);
}
}
return tex;
}
void
SCR_ScreenShot_f (void)
{
dstring_t *pcxname = dstring_new ();
pcx_t *pcx;
int pcx_len;
// find a file name to save it to
if (!QFS_NextFilename (pcxname,
va (0, "%s/qf", qfs_gamedir->dir.shots), ".pcx")) {
Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PCX");
} else {
// save the pcx file
pcx = EncodePCX (vid.buffer, vid.width, vid.height, vid.rowbytes,
vid.basepal, false, &pcx_len);
QFS_WriteFile (pcxname->str, pcx, pcx_len);
Sys_Printf ("Wrote %s/%s\n", qfs_userpath, pcxname->str);
}
dstring_delete (pcxname);
}
void
R_RenderFrame (SCR_Func *scr_funcs)
{

View file

@ -88,48 +88,6 @@ sw32_SCR_CaptureBGR (void)
return tex;
}
__attribute__((const)) tex_t *
sw32_SCR_ScreenShot (unsigned width, unsigned height)
{
return 0;
}
void
sw32_SCR_ScreenShot_f (void)
{
dstring_t *pcxname = dstring_new ();
pcx_t *pcx = 0;
int pcx_len;
// find a file name to save it to
if (!QFS_NextFilename (pcxname, va (0, "%s/qf",
qfs_gamedir->dir.shots), ".pcx")) {
Sys_Printf ("SCR_ScreenShot_f: Couldn't create a PCX");
} else {
// save the pcx file
switch(sw32_ctx->pixbytes) {
case 1:
pcx = EncodePCX (vid.buffer, vid.width, vid.height, vid.rowbytes,
vid.basepal, false, &pcx_len);
break;
case 2:
Sys_Printf("SCR_ScreenShot_f: FIXME - add 16bit support\n");
break;
case 4:
Sys_Printf("SCR_ScreenShot_f: FIXME - add 32bit support\n");
break;
default:
Sys_Error("SCR_ScreenShot_f: unsupported r_pixbytes %i", sw32_ctx->pixbytes);
}
if (pcx) {
QFS_WriteFile (pcxname->str, pcx, pcx_len);
Sys_Printf ("Wrote %s/%s\n", qfs_userpath, pcxname->str);
}
}
dstring_delete (pcxname);
}
void
sw32_R_RenderFrame (SCR_Func *scr_funcs)
{

View file

@ -132,8 +132,6 @@ vid_render_funcs_t gl_vid_render_funcs = {
gl_Draw_SubPic,
gl_SCR_CaptureBGR,
gl_SCR_ScreenShot,
SCR_DrawStringToSnap,
gl_Fog_Update,
gl_Fog_ParseWorldspawn,
@ -146,7 +144,6 @@ vid_render_funcs_t gl_vid_render_funcs = {
gl_R_NewMap,
gl_R_LineGraph,
gl_R_ViewChanged,
gl_SCR_ScreenShot_f,
&model_funcs
};

View file

@ -131,8 +131,6 @@ vid_render_funcs_t glsl_vid_render_funcs = {
glsl_Draw_SubPic,
glsl_SCR_CaptureBGR,
glsl_SCR_ScreenShot,
SCR_DrawStringToSnap,
glsl_Fog_Update,
glsl_Fog_ParseWorldspawn,
@ -145,7 +143,6 @@ vid_render_funcs_t glsl_vid_render_funcs = {
glsl_R_NewMap,
glsl_R_LineGraph,
glsl_R_ViewChanged,
glsl_SCR_ScreenShot_f,
&model_funcs
};

View file

@ -128,8 +128,6 @@ vid_render_funcs_t sw_vid_render_funcs = {
Draw_SubPic,
SCR_CaptureBGR,
SCR_ScreenShot,
SCR_DrawStringToSnap,
0,
0,
@ -142,7 +140,6 @@ vid_render_funcs_t sw_vid_render_funcs = {
R_NewMap,
R_LineGraph,
R_ViewChanged,
SCR_ScreenShot_f,
&model_funcs
};

View file

@ -133,8 +133,6 @@ vid_render_funcs_t sw32_vid_render_funcs = {
sw32_Draw_SubPic,
sw32_SCR_CaptureBGR,
sw32_SCR_ScreenShot,
SCR_DrawStringToSnap,
0,
0,
@ -147,7 +145,6 @@ vid_render_funcs_t sw32_vid_render_funcs = {
sw32_R_NewMap,
sw32_R_LineGraph,
sw32_R_ViewChanged,
sw32_SCR_ScreenShot_f,
&model_funcs
};

View file

@ -70,18 +70,6 @@
static vulkan_ctx_t *vulkan_ctx;
static tex_t *
vulkan_SCR_CaptureBGR (void)
{
return 0;
}
static tex_t *
vulkan_SCR_ScreenShot (unsigned width, unsigned height)
{
return 0;
}
static void
vulkan_Fog_Update (float density, float red, float green, float blue,
float time)
@ -442,14 +430,16 @@ capture_screenshot (const byte *data, int width, int height)
dstring_delete (name);
}
static void
vulkan_SCR_ScreenShot_f (void)
static tex_t *
vulkan_SCR_CaptureBGR (void)
{
if (!vulkan_ctx->capture) {
Sys_Printf ("Screenshot not supported\n");
return;
Sys_Printf ("Capture not supported\n");
return 0;
}
vulkan_ctx->capture_callback = capture_screenshot;
//FIXME async process
return 0;
}
static void
@ -662,8 +652,6 @@ vid_render_funcs_t vulkan_vid_render_funcs = {
vulkan_Draw_SubPic,
vulkan_SCR_CaptureBGR,
vulkan_SCR_ScreenShot,
SCR_DrawStringToSnap,
vulkan_Fog_Update,
vulkan_Fog_ParseWorldspawn,
@ -676,7 +664,6 @@ vid_render_funcs_t vulkan_vid_render_funcs = {
vulkan_R_NewMap,
vulkan_R_LineGraph,
vulkan_R_ViewChanged,
vulkan_SCR_ScreenShot_f,
&model_funcs
};

View file

@ -61,25 +61,22 @@ CL_RSShot_f (void)
Sys_Printf ("Remote screen shot requested.\n");
tex = r_funcs->SCR_ScreenShot (RSSHOT_WIDTH, RSSHOT_HEIGHT);
tex = SCR_SnapScreen (RSSHOT_WIDTH, RSSHOT_HEIGHT);
if (tex) {
time (&now);
st = dstring_strdup (ctime (&now));
dstring_snip (st, strlen (st->str) - 1, 1);
r_funcs->SCR_DrawStringToSnap (st->str, tex,
tex->width - strlen (st->str) * 8,
tex->height - 1);
SCR_DrawStringToSnap (st->str, tex, tex->width - strlen (st->str) * 8,
tex->height - 1);
dstring_copystr (st, cls.servername->str);
r_funcs->SCR_DrawStringToSnap (st->str, tex,
tex->width - strlen (st->str) * 8,
tex->height - 11);
SCR_DrawStringToSnap (st->str, tex, tex->width - strlen (st->str) * 8,
tex->height - 11);
dstring_copystr (st, cl_name->string);
r_funcs->SCR_DrawStringToSnap (st->str, tex,
tex->width - strlen (st->str) * 8,
tex->height - 21);
SCR_DrawStringToSnap (st->str, tex, tex->width - strlen (st->str) * 8,
tex->height - 21);
pcx = EncodePCX (tex->data, tex->width, tex->height, tex->width,
r_data->vid->basepal, true, &pcx_len);