Skip to content

Commit 9664cd4

Browse files
committed
Restore screenshot functionality and Targa saving
With the migration to OpenGL ES2 from OpenGL 1.1 some parameters of `glReadPixels` became more restrictive, only allowing two distinct pairs of format/type; neither of which matched the format we needed to capture screenshots. This change reads out pixels in an always-supported format (RGBA) and converts it to the desired RGB as it flips the image. Stubbed out functionality to write Targa files was also reimplemented via stb_image.
1 parent 9a1ae7b commit 9664cd4

2 files changed

Lines changed: 60 additions & 25 deletions

File tree

engine/core/core_image.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ image_c::~image_c()
5050

5151
void image_c::CopyRaw(int inType, dword inWidth, dword inHeight, const byte* inDat)
5252
{
53-
if (dat) delete dat;
53+
if (dat) delete[] dat;
5454
comp = inType & 0xF;
5555
type = inType;
5656
width = inWidth;
@@ -61,7 +61,7 @@ void image_c::CopyRaw(int inType, dword inWidth, dword inHeight, const byte* inD
6161

6262
void image_c::Free()
6363
{
64-
delete dat;
64+
delete[] dat;
6565
dat = NULL;
6666
}
6767

@@ -188,7 +188,7 @@ bool targa_c::Load(const char* fileName)
188188
int rlen = ((rlehdr & 0x7F) + 1) * comp;
189189
if (x + rlen > rowSize) {
190190
con->Warning("TGA '%s': invalid RLE coding (overlong row)", fileName);
191-
delete dat;
191+
delete[] dat;
192192
return true;
193193
}
194194
if (rlehdr & 0x80) {
@@ -225,7 +225,22 @@ bool targa_c::Load(const char* fileName)
225225

226226
bool targa_c::Save(const char* fileName)
227227
{
228-
return true;
228+
if (type != IMGTYPE_RGB && type != IMGTYPE_RGBA) {
229+
return true;
230+
}
231+
232+
// Open file
233+
fileOutputStream_c out;
234+
if (out.FileOpen(fileName, true)) {
235+
return true;
236+
}
237+
238+
auto rc = stbi_write_tga_to_func([](void* ctx, void* data, int size) {
239+
auto out = (fileOutputStream_c*)ctx;
240+
out->Write(data, size);
241+
}, &out, width, height, comp, dat);
242+
243+
return !rc;
229244
}
230245

231246
bool targa_c::ImageInfo(const char* fileName, imageInfo_s* info)
@@ -277,14 +292,14 @@ bool jpeg_c::Load(const char* fileName)
277292
return true;
278293
}
279294
int x, y, in_comp;
280-
if (!stbi_info_from_memory(fileData.data(), fileData.size(), &x, &y, &in_comp)) {
295+
if (!stbi_info_from_memory(fileData.data(), (int)fileData.size(), &x, &y, &in_comp)) {
281296
return true;
282297
}
283298
if (in_comp != 1 && in_comp != 3) {
284299
con->Warning("JPEG '%s': unsupported component count '%d'", fileName, comp);
285300
return true;
286301
}
287-
stbi_uc* data = stbi_load_from_memory(fileData.data(), fileData.size(), &x, &y, &in_comp, in_comp);
302+
stbi_uc* data = stbi_load_from_memory(fileData.data(), (int)fileData.size(), &x, &y, &in_comp, in_comp);
288303
if (!data) {
289304
stbi_image_free(data);
290305
return true;
@@ -335,7 +350,7 @@ bool jpeg_c::ImageInfo(const char* fileName, imageInfo_s* info)
335350
return true;
336351
}
337352
int x, y, comp;
338-
if (stbi_info_from_memory(fileData.data(), fileData.size(), &x, &y, &comp)) {
353+
if (stbi_info_from_memory(fileData.data(), (int)fileData.size(), &x, &y, &comp)) {
339354
return true;
340355
}
341356

@@ -366,14 +381,14 @@ bool png_c::Load(const char* fileName)
366381
return true;
367382
}
368383
int x, y, in_comp;
369-
if (!stbi_info_from_memory(fileData.data(), fileData.size(), &x, &y, &in_comp)) {
384+
if (!stbi_info_from_memory(fileData.data(), (int)fileData.size(), &x, &y, &in_comp)) {
370385
return true;
371386
}
372387
width = x;
373388
height = y;
374389
comp = (in_comp == 1 || in_comp == 3) ? 3 : 4;
375390
type = comp == 3 ? IMGTYPE_RGB : IMGTYPE_RGBA;
376-
stbi_uc* data = stbi_load_from_memory(fileData.data(), fileData.size(), &x, &y, &in_comp, comp);
391+
stbi_uc* data = stbi_load_from_memory(fileData.data(), (int)fileData.size(), &x, &y, &in_comp, comp);
377392
if (!data) {
378393
stbi_image_free(data);
379394
return true;
@@ -420,7 +435,7 @@ bool png_c::ImageInfo(const char* fileName, imageInfo_s* info)
420435
return true;
421436
}
422437
int x, y, comp;
423-
if (stbi_info_from_memory(fileData.data(), fileData.size(), &x, &y, &comp)) {
438+
if (stbi_info_from_memory(fileData.data(), (int)fileData.size(), &x, &y, &comp)) {
424439
return true;
425440
}
426441

@@ -449,7 +464,7 @@ bool gif_c::Load(const char* fileName)
449464
return true;
450465
}
451466
int x, y, in_comp;
452-
stbi_uc* data = stbi_load_from_memory(fileData.data(), fileData.size(), &x, &y, &in_comp, 4);
467+
stbi_uc* data = stbi_load_from_memory(fileData.data(), (int)fileData.size(), &x, &y, &in_comp, 4);
453468
if (!data || in_comp != 4) {
454469
stbi_image_free(data);
455470
return true;

engine/render/r_main.cpp

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,25 +1830,45 @@ void r_renderer_c::C_Screenshot(IConsole* conHnd, args_c& args)
18301830

18311831
void r_renderer_c::DoScreenshot(image_c* i, const char* ext)
18321832
{
1833-
int xs = sys->video->vid.size[0];
1834-
int ys = sys->video->vid.size[1];
1833+
if (i->type != IMGTYPE_RGB) {
1834+
return;
1835+
}
1836+
auto& rt = rttMain[presentRtt];
1837+
int const xs = rt.width;
1838+
int const ys = rt.height;
18351839

1836-
int size = xs * ys * 3;
1837-
byte* sbuf = new byte[size];
1840+
// Pixel reading only supports RGBA and an implementation-specific format.
1841+
// Use RGBA for convenience as that's close enough to what we want to save in the end.
1842+
int const readSize = xs * ys * 4;
1843+
int const writeSize = xs * ys * 3;
1844+
std::vector<byte> sbuf(readSize);
18381845

18391846
// Read the front buffer
1847+
GLint oldFb{};
1848+
GLenum oglErr = glGetError();
1849+
GLenum implColorReadFormat{}, implColorReadType{};
1850+
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFb);
1851+
glBindFramebuffer(GL_FRAMEBUFFER, rttMain[presentRtt].framebuffer);
18401852
glPixelStorei(GL_PACK_ALIGNMENT, 1);
1841-
glReadPixels(0, 0, xs, ys, r_tex_c::GLTypeForImgType(i->type), GL_UNSIGNED_BYTE, sbuf);
1842-
1843-
// Flip the image
1844-
int span = xs * 3;
1845-
byte* ss = new byte[size];
1846-
byte* p1 = sbuf;
1847-
byte* p2 = ss + size - span;
1848-
for (int y = 0; y < ys; y++, p1 += span, p2 -= span) {
1849-
memcpy(p2, p1, span);
1853+
glReadPixels(0, 0, xs, ys, GL_RGBA, GL_UNSIGNED_BYTE, sbuf.data());
1854+
oglErr = glGetError();
1855+
glBindFramebuffer(GL_FRAMEBUFFER, oldFb);
1856+
1857+
// Flip and convert the image to RGB
1858+
int const readSpan = xs * 4;
1859+
int const writeSpan = xs * 3;
1860+
byte* ss = new byte[writeSize]; // This is a raw pointer as ownership is taken by the image object.
1861+
byte* p1 = sbuf.data();
1862+
byte* p2 = ss + writeSize - writeSpan;
1863+
for (int y = 0; y < ys; ++y, p2 -= writeSpan * 2) {
1864+
for (int x = 0; x < xs; ++x) {
1865+
*p2++ = *p1++; // R
1866+
*p2++ = *p1++; // G
1867+
*p2++ = *p1++; // B
1868+
p1++; // A
1869+
}
18501870
}
1851-
delete[] sbuf;
1871+
sbuf.clear();
18521872

18531873
// Set image info
18541874
i->dat = ss;

0 commit comments

Comments
 (0)