Save .rdc file extthumbnail as PNG

In order to embed the original rendered image during the capture in its
original form, without incurring JPEG pixel artifacts, use PNG file
format for thumbnails.

Change-Id: I14ff487856949cd6f83889507e16feb4c50a40fb
This commit is contained in:
Alex Vakulenko
2018-08-31 15:27:22 -07:00
committed by Baldur Karlsson
parent 3233afd390
commit 264b296b7d
7 changed files with 283 additions and 495 deletions
+149 -15
View File
@@ -29,9 +29,11 @@
#include "api/replay/version.h"
#include "common/common.h"
#include "hooks/hooks.h"
#include "maths/formatpacking.h"
#include "replay/replay_driver.h"
#include "serialise/rdcfile.h"
#include "serialise/serialiser.h"
#include "stb/stb_image_write.h"
#include "strings/string_utils.h"
#include "crash_handler.h"
@@ -716,8 +718,142 @@ bool RenderDoc::ShouldTriggerCapture(uint32_t frameNumber)
return ret;
}
RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, void *thpixels, size_t thlen,
uint16_t thwidth, uint16_t thheight, FileType thformat)
void RenderDoc::ResamplePixels(const FramePixels &in, RDCThumb &out)
{
// code below assumes pitch_requirement is a power of 2 number
RDCASSERT((in.pitch_requirement & (in.pitch_requirement - 1)) == 0);
out.width = (uint16_t)RDCMIN(in.max_width, in.width);
out.width &= ~(in.pitch_requirement - 1); // align down to multiple of in.
out.height = uint16_t(out.width * in.height / in.width);
out.len = 3 * out.width * out.height;
out.pixels = new byte[out.len];
out.format = FileType::Raw;
byte *dst = (byte *)out.pixels;
byte *source = (byte *)in.data;
for(uint32_t y = 0; y < out.height; y++)
{
for(uint32_t x = 0; x < out.width; x++)
{
uint32_t xSource = x * in.width / out.width;
uint32_t ySource = y * in.height / out.height;
byte *src = &source[in.stride * xSource + in.pitch * ySource];
if(in.buf1010102)
{
uint32_t *src1010102 = (uint32_t *)src;
Vec4f unorm = ConvertFromR10G10B10A2(*src1010102);
dst[0] = (byte)(unorm.x * 255.0f);
dst[1] = (byte)(unorm.y * 255.0f);
dst[2] = (byte)(unorm.z * 255.0f);
}
else if(in.buf565)
{
uint16_t *src565 = (uint16_t *)src;
Vec3f unorm = ConvertFromB5G6R5(*src565);
dst[0] = (byte)(unorm.z * 255.0f);
dst[1] = (byte)(unorm.y * 255.0f);
dst[2] = (byte)(unorm.x * 255.0f);
}
else if(in.buf5551)
{
uint16_t *src5551 = (uint16_t *)src;
Vec4f unorm = ConvertFromB5G5R5A1(*src5551);
dst[0] = (byte)(unorm.z * 255.0f);
dst[1] = (byte)(unorm.y * 255.0f);
dst[2] = (byte)(unorm.x * 255.0f);
}
else if(in.bgra)
{
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
}
else if(in.bpc == 2) // R16G16B16A16 backbuffer
{
uint16_t *src16 = (uint16_t *)src;
float linearR = RDCCLAMP(ConvertFromHalf(src16[0]), 0.0f, 1.0f);
float linearG = RDCCLAMP(ConvertFromHalf(src16[1]), 0.0f, 1.0f);
float linearB = RDCCLAMP(ConvertFromHalf(src16[2]), 0.0f, 1.0f);
if(linearR < 0.0031308f)
dst[0] = byte(255.0f * (12.92f * linearR));
else
dst[0] = byte(255.0f * (1.055f * powf(linearR, 1.0f / 2.4f) - 0.055f));
if(linearG < 0.0031308f)
dst[1] = byte(255.0f * (12.92f * linearG));
else
dst[1] = byte(255.0f * (1.055f * powf(linearG, 1.0f / 2.4f) - 0.055f));
if(linearB < 0.0031308f)
dst[2] = byte(255.0f * (12.92f * linearB));
else
dst[2] = byte(255.0f * (1.055f * powf(linearB, 1.0f / 2.4f) - 0.055f));
}
else
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
dst += 3;
}
}
if(!in.is_y_flipped)
{
for(uint16_t y = 0; y <= out.height / 2; y++)
{
uint16_t flipY = (out.height - 1 - y);
for(uint16_t x = 0; x < out.width; x++)
{
byte *src = (byte *)out.pixels;
byte save[3];
save[0] = src[(y * out.width + x) * 3 + 0];
save[1] = src[(y * out.width + x) * 3 + 1];
save[2] = src[(y * out.width + x) * 3 + 2];
src[(y * out.width + x) * 3 + 0] = src[(flipY * out.width + x) * 3 + 0];
src[(y * out.width + x) * 3 + 1] = src[(flipY * out.width + x) * 3 + 1];
src[(y * out.width + x) * 3 + 2] = src[(flipY * out.width + x) * 3 + 2];
src[(flipY * out.width + x) * 3 + 0] = save[0];
src[(flipY * out.width + x) * 3 + 1] = save[1];
src[(flipY * out.width + x) * 3 + 2] = save[2];
}
}
}
}
void RenderDoc::EncodePixelsPNG(const RDCThumb &in, RDCThumb &out)
{
struct WriteCallbackData
{
std::vector<byte> buffer;
static void writeData(void *context, void *data, int size)
{
WriteCallbackData *pThis = (WriteCallbackData *)context;
const byte *start = (const byte *)data;
pThis->buffer.insert(pThis->buffer.end(), start, start + size);
}
};
WriteCallbackData callbackData;
stbi_write_png_to_func(&WriteCallbackData::writeData, &callbackData, in.width, in.height, 3,
in.pixels, 0);
out.width = in.width;
out.height = in.height;
out.pixels = new byte[callbackData.buffer.size()];
memcpy((void *)out.pixels, callbackData.buffer.data(), callbackData.buffer.size());
out.len = (uint32_t)callbackData.buffer.size();
out.format = FileType::PNG;
}
RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, const FramePixels &fp)
{
RDCFile *ret = new RDCFile;
@@ -737,22 +873,17 @@ RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, void *thpixel
}
}
RDCThumb th;
RDCThumb *thumb = NULL;
if(thpixels)
RDCThumb outRaw, outPng;
if(fp.data)
{
th.len = (uint32_t)thlen;
th.pixels = (const byte *)thpixels;
th.width = thwidth;
th.height = thheight;
RDCASSERT(thformat == FileType::JPG || thformat == FileType::PNG || thformat == FileType::BMP ||
thformat == FileType::TGA || thformat == FileType::Raw);
th.format = thformat;
thumb = &th;
// point sample info into raw buffer
ResamplePixels(fp, outRaw);
EncodePixelsPNG(outRaw, outPng);
}
ret->SetData(driver, ToStr(driver).c_str(), OSUtility::GetMachineIdent(), thumb);
RDCASSERT(outPng.pixels != NULL);
ret->SetData(driver, ToStr(driver).c_str(), OSUtility::GetMachineIdent(), &outPng);
FileIO::CreateParentDirectory(m_CurrentLogFile);
@@ -764,6 +895,9 @@ RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, void *thpixel
SAFE_DELETE(ret);
}
SAFE_DELETE_ARRAY(outRaw.pixels);
SAFE_DELETE_ARRAY(outPng.pixels);
return ret;
}