mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-23 22:25:55 +00:00
Perform image processing at 32bpp for HDR/EXR/DDS outputs. Closes #1103
* When flattening array slices or remapping to a cube cruciform, we can remap to RGBA32 when the output format is itself greater than 8bpp, to ensure we don't lose quality before output.
This commit is contained in:
@@ -790,20 +790,41 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
td.format.type == ResourceFormatType::ASTC)
|
||||
downcast = true;
|
||||
|
||||
// for DDS don't downcast, for non-HDR always downcast if we're not already RGBA8 unorm
|
||||
// for HDR&EXR we can convert from most regular types as well as 10.10.10.2 and 11.11.10
|
||||
if((sd.destType != FileType::DDS && sd.destType != FileType::HDR && sd.destType != FileType::EXR &&
|
||||
(td.format.compByteWidth != 1 || td.format.compCount != 4 ||
|
||||
td.format.compType != CompType::UNorm || td.format.bgraOrder)) ||
|
||||
downcast || (sd.destType != FileType::DDS && td.format.Special() &&
|
||||
td.format.type != ResourceFormatType::R10G10B10A2 &&
|
||||
td.format.type != ResourceFormatType::R11G11B10))
|
||||
{
|
||||
// for non-HDR always downcast if we're not already RGBA8 unorm
|
||||
if(sd.destType != FileType::DDS && sd.destType != FileType::HDR && sd.destType != FileType::EXR &&
|
||||
(td.format.compByteWidth != 1 || td.format.compCount != 4 ||
|
||||
td.format.compType != CompType::UNorm || td.format.bgraOrder))
|
||||
downcast = true;
|
||||
td.format.compByteWidth = 1;
|
||||
td.format.compCount = 4;
|
||||
td.format.compType = CompType::UNorm;
|
||||
td.format.type = ResourceFormatType::Regular;
|
||||
|
||||
// for HDR & EXR we can convert from most regular types as well as 10.10.10.2 and 11.11.10
|
||||
if(sd.destType != FileType::DDS && td.format.Special() &&
|
||||
td.format.type != ResourceFormatType::R10G10B10A2 &&
|
||||
td.format.type != ResourceFormatType::R11G11B10)
|
||||
downcast = true;
|
||||
|
||||
// if we're downcasting, pick either RGBA8 or RGBA32 to downcast to
|
||||
RemapTexture remap = RemapTexture::NoRemap;
|
||||
|
||||
if(downcast)
|
||||
{
|
||||
// if the source and destination are more than 1 byte per component, remap to RGBA32
|
||||
if(td.format.compByteWidth > 1 && (sd.destType == FileType::DDS ||
|
||||
sd.destType == FileType::HDR || sd.destType == FileType::EXR))
|
||||
{
|
||||
remap = RemapTexture::RGBA32;
|
||||
td.format.compByteWidth = 4;
|
||||
td.format.compCount = 4;
|
||||
td.format.compType = CompType::Float;
|
||||
td.format.type = ResourceFormatType::Regular;
|
||||
}
|
||||
else
|
||||
{
|
||||
remap = RemapTexture::RGBA8;
|
||||
td.format.compByteWidth = 1;
|
||||
td.format.compCount = 4;
|
||||
td.format.compType = CompType::UNorm;
|
||||
td.format.type = ResourceFormatType::Regular;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rowPitch = 0;
|
||||
@@ -868,7 +889,7 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
params.forDiskSave = true;
|
||||
params.typeHint = sd.typeHint;
|
||||
params.resolve = resolveSamples;
|
||||
params.remap = downcast ? RemapTexture::RGBA8 : RemapTexture::NoRemap;
|
||||
params.remap = remap;
|
||||
params.blackPoint = sd.comp.blackPoint;
|
||||
params.whitePoint = sd.comp.whitePoint;
|
||||
|
||||
@@ -940,8 +961,9 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
}
|
||||
}
|
||||
|
||||
// should have been handled above, but verify incoming data is RGBA8
|
||||
if(sd.slice.slicesAsGrid && td.format.compByteWidth == 1 && td.format.compCount == 4)
|
||||
// should have been handled above, but verify incoming data is RGBA8 or RGBA32
|
||||
if(sd.slice.slicesAsGrid && (td.format.compByteWidth == 1 || td.format.compByteWidth == 4) &&
|
||||
td.format.compCount == 4 && !td.format.Special())
|
||||
{
|
||||
uint32_t sliceWidth = td.width;
|
||||
uint32_t sliceHeight = td.height;
|
||||
@@ -953,9 +975,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
td.width *= sd.slice.sliceGridWidth;
|
||||
td.height *= sliceGridHeight;
|
||||
|
||||
byte *combinedData = new byte[td.width * td.height * td.format.compCount];
|
||||
uint32_t pixelStride = td.format.compCount * td.format.compByteWidth;
|
||||
|
||||
memset(combinedData, 0, td.width * td.height * td.format.compCount);
|
||||
byte *combinedData = new byte[td.width * td.height * pixelStride];
|
||||
|
||||
memset(combinedData, 0, td.width * td.height * pixelStride);
|
||||
|
||||
for(size_t i = 0; i < subdata.size(); i++)
|
||||
{
|
||||
@@ -969,10 +993,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
{
|
||||
for(uint32_t x = 0; x < sliceWidth; x++)
|
||||
{
|
||||
uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * 4 + 0];
|
||||
uint32_t *dstpix = (uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * 4 + 0];
|
||||
uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * pixelStride + 0];
|
||||
uint32_t *dstpix =
|
||||
(uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * pixelStride + 0];
|
||||
|
||||
*dstpix = *srcpix;
|
||||
memcpy(dstpix, srcpix, pixelStride);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,9 +1009,9 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
rowPitch = td.width * 4;
|
||||
}
|
||||
|
||||
// should have been handled above, but verify incoming data is RGBA8 and 6 slices
|
||||
if(sd.slice.cubeCruciform && td.format.compByteWidth == 1 && td.format.compCount == 4 &&
|
||||
subdata.size() == 6)
|
||||
// should have been handled above, but verify incoming data is RGBA8 or RGBA32 and 6 slices
|
||||
if(sd.slice.cubeCruciform && (td.format.compByteWidth == 1 || td.format.compByteWidth == 4) &&
|
||||
td.format.compCount == 4 && !td.format.Special() && subdata.size() == 6)
|
||||
{
|
||||
uint32_t sliceWidth = td.width;
|
||||
uint32_t sliceHeight = td.height;
|
||||
@@ -994,9 +1019,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
td.width *= 4;
|
||||
td.height *= 3;
|
||||
|
||||
byte *combinedData = new byte[td.width * td.height * td.format.compCount];
|
||||
uint32_t pixelStride = td.format.compCount * td.format.compByteWidth;
|
||||
|
||||
memset(combinedData, 0, td.width * td.height * td.format.compCount);
|
||||
byte *combinedData = new byte[td.width * td.height * pixelStride];
|
||||
|
||||
memset(combinedData, 0, td.width * td.height * pixelStride);
|
||||
|
||||
/*
|
||||
Y X=0 1 2 3
|
||||
@@ -1025,10 +1052,11 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
{
|
||||
for(uint32_t x = 0; x < sliceWidth; x++)
|
||||
{
|
||||
uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * 4 + 0];
|
||||
uint32_t *dstpix = (uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * 4 + 0];
|
||||
uint32_t *srcpix = (uint32_t *)&subdata[i][(y * sliceWidth + x) * pixelStride + 0];
|
||||
uint32_t *dstpix =
|
||||
(uint32_t *)&combinedData[((y + yoffs) * td.width + x + xoffs) * pixelStride + 0];
|
||||
|
||||
*dstpix = *srcpix;
|
||||
memcpy(dstpix, srcpix, pixelStride);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,24 +1073,42 @@ bool ReplayController::SaveTexture(const TextureSave &saveData, const char *path
|
||||
// if we want a grayscale image of one channel, splat it across all channels
|
||||
// and set alpha to full
|
||||
if(sd.channelExtract >= 0 && td.format.type == ResourceFormatType::Regular &&
|
||||
td.format.compByteWidth == 1 && (uint32_t)sd.channelExtract < td.format.compCount)
|
||||
(td.format.compByteWidth == 1 || td.format.compByteWidth == 4) &&
|
||||
(uint32_t)sd.channelExtract < td.format.compCount)
|
||||
{
|
||||
uint32_t cc = td.format.compCount;
|
||||
uint32_t pixelStride = td.format.compCount * td.format.compByteWidth;
|
||||
uint32_t compWidth = td.format.compByteWidth;
|
||||
uint32_t compCount = td.format.compCount;
|
||||
|
||||
uint32_t val = 0;
|
||||
uint32_t max = ~0U;
|
||||
|
||||
for(uint32_t y = 0; y < td.height; y++)
|
||||
{
|
||||
for(uint32_t x = 0; x < td.width; x++)
|
||||
{
|
||||
subdata[0][(y * td.width + x) * cc + 0] =
|
||||
subdata[0][(y * td.width + x) * cc + sd.channelExtract];
|
||||
if(cc >= 2)
|
||||
subdata[0][(y * td.width + x) * cc + 1] =
|
||||
subdata[0][(y * td.width + x) * cc + sd.channelExtract];
|
||||
if(cc >= 3)
|
||||
subdata[0][(y * td.width + x) * cc + 2] =
|
||||
subdata[0][(y * td.width + x) * cc + sd.channelExtract];
|
||||
if(cc >= 4)
|
||||
subdata[0][(y * td.width + x) * cc + 3] = 255;
|
||||
memcpy(&val, &subdata[0][(y * td.width + x) * pixelStride + sd.channelExtract * compWidth],
|
||||
td.format.compByteWidth);
|
||||
|
||||
switch(compCount)
|
||||
{
|
||||
case 4:
|
||||
memcpy(&subdata[0][(y * td.width + x) * pixelStride + 3 * compWidth], &max,
|
||||
td.format.compByteWidth);
|
||||
// deliberate fallthrough
|
||||
case 3:
|
||||
memcpy(&subdata[0][(y * td.width + x) * pixelStride + 2 * compWidth], &val,
|
||||
td.format.compByteWidth);
|
||||
// deliberate fallthrough
|
||||
case 2:
|
||||
memcpy(&subdata[0][(y * td.width + x) * pixelStride + 1 * compWidth], &val,
|
||||
td.format.compByteWidth);
|
||||
// deliberate fallthrough
|
||||
case 1:
|
||||
memcpy(&subdata[0][(y * td.width + x) * pixelStride + 0 * compWidth], &val,
|
||||
td.format.compByteWidth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user