Added new screenshot formats.

Added the ability to change the screenshot format with a new CVar:
r_screenshotFormat. This has four options: 0 (TGA), 1 (BMP), 2 (PNG),
and 3 (JPG). JPG quality can be selected with a new CVar:
r_screenshotJpgQuality. TGA is the default to preserve original behavior
from DOOM 3.

A new dependency on stb_image_write has been added. It has been
included. No additional configuration with CMake is required.
This commit is contained in:
Nick Whitlock 2023-05-22 09:40:11 -04:00
parent b6365e91d7
commit 7ea3e91474
2 changed files with 1800 additions and 29 deletions

View file

@ -47,6 +47,9 @@ If you have questions concerning this license or the applicable additional terms
#include "sys/win32/win_local.h"
#endif
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
// functions that are not called every frame
glconfig_t glConfig;
@ -230,6 +233,8 @@ idCVar r_scaleMenusTo43( "r_scaleMenusTo43", "1", CVAR_RENDERER | CVAR_ARCHIVE |
// DG: the fscking patent has finally expired
idCVar r_useCarmacksReverse( "r_useCarmacksReverse", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Use Z-Fail (Carmack's Reverse) when rendering shadows" );
idCVar r_useStencilOpSeparate( "r_useStencilOpSeparate", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Use glStencilOpSeparate() (if available) when rendering shadows" );
idCVar r_screenshotFormat("r_screenshotFormat", "0", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "Screenshot format. 0 = TGA (default), 1 = BMP, 2 = PNG, 3 = JPG");
idCVar r_screenshotJpgQuality("r_screenshotJpgQuality", "75", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "Screenshot quality for JPG images (0-100)");
// define qgl functions
#define QGLPROC(name, rettype, args) rettype (APIENTRYP q##name) args;
@ -1249,6 +1254,18 @@ void R_ReadTiledPixels( int width, int height, byte *buffer, renderView_t *ref =
glConfig.vidHeight = oldHeight;
}
/*
==================
WriteScreenshot
Callback to each screenshot function
==================
*/
void WriteScreenshot(void* context, void* data, int size)
{
idFile* f = (idFile*)context;
f->Write(data, size);
}
/*
==================
@ -1262,66 +1279,80 @@ If ref == NULL, session->updateScreen will be used
==================
*/
void idRenderSystemLocal::TakeScreenshot( int width, int height, const char *fileName, int blends, renderView_t *ref ) {
byte *buffer;
int i, j, c, temp;
byte *buffer, *flippedBuffer;
int i, j, k;
takingScreenshot = true;
int pix = width * height;
buffer = (byte *)R_StaticAlloc(pix*3 + 18);
memset (buffer, 0, 18);
buffer = (byte *)R_StaticAlloc(pix*3);
flippedBuffer = (byte*)R_StaticAlloc(pix*3);
if ( blends <= 1 ) {
R_ReadTiledPixels( width, height, buffer + 18, ref );
R_ReadTiledPixels( width, height, buffer, ref );
} else {
unsigned short *shortBuffer = (unsigned short *)R_StaticAlloc(pix*2*3);
memset (shortBuffer, 0, pix*2*3);
memset( shortBuffer, 0, pix*2*3 );
// enable anti-aliasing jitter
r_jitter.SetBool( true );
for ( i = 0 ; i < blends ; i++ ) {
R_ReadTiledPixels( width, height, buffer + 18, ref );
R_ReadTiledPixels( width, height, buffer, ref );
for ( j = 0 ; j < pix*3 ; j++ ) {
shortBuffer[j] += buffer[18+j];
shortBuffer[j] += buffer[j];
}
}
// divide back to bytes
for ( i = 0 ; i < pix*3 ; i++ ) {
buffer[18+i] = shortBuffer[i] / blends;
buffer[i] = shortBuffer[i] / blends;
}
R_StaticFree( shortBuffer );
r_jitter.SetBool( false );
}
// fill in the header (this is vertically flipped, which qglReadPixels emits)
buffer[2] = 2; // uncompressed type
buffer[12] = width & 255;
buffer[13] = width >> 8;
buffer[14] = height & 255;
buffer[15] = height >> 8;
buffer[16] = 24; // pixel size
// swap rgb to bgr
c = 18 + width * height * 3;
for (i=18 ; i<c ; i+=3) {
temp = buffer[i];
buffer[i] = buffer[i+2];
buffer[i+2] = temp;
// The buffer is upside down, we need to flip it the right way.
for (i = 0; i < width; i++) {
for (j = 0; j < height; j++) {
for (k = 0; k < 3; k++) {
flippedBuffer[(i + j * width) * 3 + k] = buffer[(i + (height - 1 - j) * width) * 3 + k];
}
}
}
// _D3XP adds viewnote screenie save to cdpath
if ( strstr( fileName, "viewnote" ) ) {
fileSystem->WriteFile( fileName, buffer, c, "fs_cdpath" );
} else {
fileSystem->WriteFile( fileName, buffer, c );
idFile* f;
if (strstr(fileName, "viewnote")) {
f = fileSystem->OpenFileWrite( fileName, "fs_cdpath" );
}
else {
f = fileSystem->OpenFileWrite( fileName );
}
switch (cvarSystem->GetCVarInteger( "r_screenshotFormat" ))
{
default:
stbi_write_tga_to_func( WriteScreenshot, f, width, height, 3, flippedBuffer );
break;
case 1:
stbi_write_bmp_to_func( WriteScreenshot, f, width, height, 3, flippedBuffer );
break;
case 2:
stbi_write_png_to_func( WriteScreenshot, f, width, height, 3, flippedBuffer, 3 * width );
break;
case 3:
stbi_write_jpg_to_func( WriteScreenshot, f, width, height, 3, flippedBuffer, idMath::ClampInt(0, 100, cvarSystem->GetCVarInteger("r_screenshotJpgQuality")) );
break;
}
fileSystem->CloseFile(f);
R_StaticFree( buffer );
R_StaticFree( flippedBuffer );
takingScreenshot = false;
@ -1344,6 +1375,8 @@ void R_ScreenshotFilename( int &lastNumber, const char *base, idStr &fileName )
bool fsrestrict = cvarSystem->GetCVarBool( "fs_restrict" );
cvarSystem->SetCVarBool( "fs_restrict", false );
int format = cvarSystem->GetCVarInteger("r_screenshotFormat");
lastNumber++;
if ( lastNumber > 99999 ) {
lastNumber = 99999;
@ -1361,7 +1394,21 @@ void R_ScreenshotFilename( int &lastNumber, const char *base, idStr &fileName )
frac -= d*10;
e = frac;
sprintf( fileName, "%s%i%i%i%i%i.tga", base, a, b, c, d, e );
switch (format) {
default:
sprintf(fileName, "%s%i%i%i%i%i.tga", base, a, b, c, d, e);
break;
case 1:
sprintf(fileName, "%s%i%i%i%i%i.bmp", base, a, b, c, d, e);
break;
case 2:
sprintf(fileName, "%s%i%i%i%i%i.png", base, a, b, c, d, e);
break;
case 3:
sprintf(fileName, "%s%i%i%i%i%i.jpg", base, a, b, c, d, e);
break;
}
if ( lastNumber == 99999 ) {
break;
}

File diff suppressed because it is too large Load diff