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:
baldurk
2018-10-17 15:38:53 +01:00
parent d06ea5b8a4
commit c45023236a
+87 -41
View File
@@ -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;
}
}
}
}