1
0
mirror of https://github.com/microsoft/DirectXTex synced 2024-11-21 20:10:05 +00:00

texconv: -rotatecolor and -nits options

This commit is contained in:
Chuck Walbourn 2018-02-07 17:14:11 -08:00
parent 46e7ad7020
commit 16e605acf5

View File

@ -102,9 +102,21 @@ enum OPTIONS
OPT_TONEMAP,
OPT_X2_BIAS,
OPT_FILELIST,
OPT_ROTATE_COLOR,
OPT_PAPER_WHITE_NITS,
OPT_MAX
};
enum
{
ROTATE_709_TO_HDR10 = 1,
ROTATE_HDR10_TO_709,
ROTATE_709_TO_2020,
ROTATE_2020_TO_709,
ROTATE_P3_TO_HDR10,
ROTATE_P3_TO_2020,
};
static_assert(OPT_MAX <= 64, "dwOptions is a DWORD64 bitfield");
struct SConversion
@ -174,6 +186,8 @@ const SValue g_pOptions[] =
{ L"tonemap", OPT_TONEMAP },
{ L"x2bias", OPT_X2_BIAS },
{ L"flist", OPT_FILELIST },
{ L"rotatecolor", OPT_ROTATE_COLOR },
{ L"nits", OPT_PAPER_WHITE_NITS },
{ nullptr, 0 }
};
@ -338,6 +352,17 @@ const SValue g_pFilters[] =
{ nullptr, TEX_FILTER_DEFAULT }
};
const SValue g_pRotateColor[] =
{
{ L"709to2020", ROTATE_709_TO_2020 },
{ L"2020to709", ROTATE_2020_TO_709 },
{ L"709toHDR10", ROTATE_709_TO_HDR10 },
{ L"HDR10to709", ROTATE_HDR10_TO_709 },
{ L"P3to2020", ROTATE_P3_TO_2020 },
{ L"P3toHDR10", ROTATE_P3_TO_HDR10 },
{ nullptr, 0 },
};
#define CODEC_DDS 0xFFFF0001
#define CODEC_TGA 0xFFFF0002
#define CODEC_HDP 0xFFFF0003
@ -697,6 +722,8 @@ namespace
L" -aw <weight> BC7 GPU compressor weighting for alpha error metric\n"
L" (defaults to 1.0)\n");
wprintf(L" -c <hex-RGB> colorkey (a.k.a. chromakey) transparency\n");
wprintf(L" -rotatecolor <rot> rotates color primaries and/or applies a curve\n");
wprintf(L" -nits <value> paper-white value in nits to use for HDR10 (defaults to 200.0)\n");
wprintf(L" -tonemap Apply a tonemap operator based on maximum luminance\n");
wprintf(L" -x2bias Enable *2 - 1 conversion cases for unorm/pos-only-float\n");
wprintf(L" -flist <filename> use text file with a list of input files (one per line)\n");
@ -707,6 +734,9 @@ namespace
wprintf(L"\n <filter>: ");
PrintList(13, g_pFilters);
wprintf(L"\n <rot>: ");
PrintList(13, g_pRotateColor);
wprintf(L"\n <filetype>: ");
PrintList(15, g_pSaveFileTypes);
@ -868,6 +898,44 @@ namespace
}
}
}
const XMVECTORF32 c_MaxNitsFor2084 = { 10000.0f, 10000.0f, 10000.0f, 1.f };
const XMMATRIX c_from709to2020 =
{
{ 0.6274040f, 0.0690970f, 0.0163916f, 0.f },
{ 0.3292820f, 0.9195400f, 0.0880132f, 0.f },
{ 0.0433136f, 0.0113612f, 0.8955950f, 0.f },
{ 0.f, 0.f, 0.f, 1.f }
};
const XMMATRIX c_from2020to709 =
{
{ 1.6604910f, -0.1245505f, -0.0181508f, 0.f },
{ -0.5876411f, 1.1328999f, -0.1005789f, 0.f },
{ -0.0728499f, -0.0083494f, 1.1187297f, 0.f },
{ 0.f, 0.f, 0.f, 1.f }
};
const XMMATRIX c_fromP3to2020 =
{
{ 0.753845f, 0.0457456f, -0.00121055f, 0.f },
{ 0.198593f, 0.941777f, 0.0176041f, 0.f },
{ 0.047562f, 0.0124772f, 0.983607f, 0.f },
{ 0.f, 0.f, 0.f, 1.f }
};
inline float LinearToST2084(float normalizedLinearValue)
{
float ST2084 = pow((0.8359375f + 18.8515625f * pow(abs(normalizedLinearValue), 0.1593017578f)) / (1.0f + 18.6875f * pow(abs(normalizedLinearValue), 0.1593017578f)), 78.84375f);
return ST2084; // Don't clamp between [0..1], so we can still perform operations on scene values higher than 10,000 nits
}
inline float ST2084ToLinear(float ST2084)
{
float normalizedLinear = pow(std::max(pow(abs(ST2084), 1.0f / 78.84375f) - 0.8359375f, 0.0f) / (18.8515625f - 18.6875f * pow(abs(ST2084), 1.0f / 78.84375f)), 1.0f / 0.1593017578f);
return normalizedLinear;
}
}
@ -896,6 +964,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
float nmapAmplitude = 1.f;
float wicQuality = -1.f;
DWORD colorKey = 0;
DWORD dwRotateColor = 0;
float paperWhiteNits = 200.f;
wchar_t szPrefix[MAX_PATH];
wchar_t szSuffix[MAX_PATH];
@ -961,6 +1031,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
case OPT_WIC_QUALITY:
case OPT_COLORKEY:
case OPT_FILELIST:
case OPT_ROTATE_COLOR:
case OPT_PAPER_WHITE_NITS:
if (!*pValue)
{
if ((iArg + 1 >= argc))
@ -1029,6 +1101,17 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
}
break;
case OPT_ROTATE_COLOR:
dwRotateColor = LookupByName(pValue, g_pRotateColor);
if (!dwRotateColor)
{
wprintf(L"Invalid value specified with -rotatecolor (%ls)\n", pValue);
wprintf(L"\n");
PrintUsage();
return 1;
}
break;
case OPT_SRGBI:
dwSRGB |= TEX_FILTER_SRGB_IN;
break;
@ -1324,6 +1407,22 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
inFile.close();
}
break;
case OPT_PAPER_WHITE_NITS:
if (swscanf_s(pValue, L"%f", &paperWhiteNits) != 1)
{
wprintf(L"Invalid value specified with -nits (%ls)\n", pValue);
wprintf(L"\n");
PrintUsage();
return 1;
}
else if (paperWhiteNits > 10000.f || paperWhiteNits <= 0.f)
{
wprintf(L"-nits (%ls) parameter must be between 0 and 10000\n", pValue);
wprintf(L"\n");
return 1;
}
break;
}
}
else if (wcspbrk(pArg, L"?*") != nullptr)
@ -1760,6 +1859,200 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
cimage.reset();
}
// --- Color rotation (if requested) -------------------------------------------
if (dwRotateColor)
{
std::unique_ptr<ScratchImage> timage(new (std::nothrow) ScratchImage);
if (!timage)
{
wprintf(L"\nERROR: Memory allocation failed\n");
return 1;
}
switch (dwRotateColor)
{
case ROTATE_709_TO_HDR10:
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[&](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t width, size_t y)
{
UNREFERENCED_PARAMETER(y);
XMVECTOR paperWhite = XMVectorReplicate(paperWhiteNits);
for (size_t j = 0; j < width; ++j)
{
XMVECTOR value = inPixels[j];
XMVECTOR nvalue = XMVector3Transform(value, c_from709to2020);
// Convert to ST.2084
nvalue = XMVectorDivide(XMVectorMultiply(nvalue, paperWhite), c_MaxNitsFor2084);
XMFLOAT4A tmp;
XMStoreFloat4A(&tmp, nvalue);
tmp.x = LinearToST2084(tmp.x);
tmp.y = LinearToST2084(tmp.y);
tmp.z = LinearToST2084(tmp.z);
nvalue = XMLoadFloat4A(&tmp);
value = XMVectorSelect(value, nvalue, g_XMSelect1110);
outPixels[j] = value;
}
}, *timage);
break;
case ROTATE_709_TO_2020:
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[&](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t width, size_t y)
{
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < width; ++j)
{
XMVECTOR value = inPixels[j];
XMVECTOR nvalue = XMVector3Transform(value, c_from709to2020);
value = XMVectorSelect(value, nvalue, g_XMSelect1110);
outPixels[j] = value;
}
}, *timage);
break;
case ROTATE_HDR10_TO_709:
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[&](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t width, size_t y)
{
UNREFERENCED_PARAMETER(y);
XMVECTOR paperWhite = XMVectorReplicate(paperWhiteNits);
for (size_t j = 0; j < width; ++j)
{
XMVECTOR value = inPixels[j];
// Convert from ST.2084
XMFLOAT4A tmp;
XMStoreFloat4A(&tmp, value);
tmp.x = ST2084ToLinear(tmp.x);
tmp.y = ST2084ToLinear(tmp.y);
tmp.z = ST2084ToLinear(tmp.z);
XMVECTOR nvalue = XMLoadFloat4A(&tmp);
nvalue = XMVectorDivide(XMVectorMultiply(nvalue, c_MaxNitsFor2084), paperWhite);
nvalue = XMVector3Transform(nvalue, c_from2020to709);
value = XMVectorSelect(value, nvalue, g_XMSelect1110);
outPixels[j] = value;
}
}, *timage);
break;
case ROTATE_2020_TO_709:
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[&](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t width, size_t y)
{
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < width; ++j)
{
XMVECTOR value = inPixels[j];
XMVECTOR nvalue = XMVector3Transform(value, c_from2020to709);
value = XMVectorSelect(value, nvalue, g_XMSelect1110);
outPixels[j] = value;
}
}, *timage);
break;
case ROTATE_P3_TO_HDR10:
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[&](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t width, size_t y)
{
UNREFERENCED_PARAMETER(y);
XMVECTOR paperWhite = XMVectorReplicate(paperWhiteNits);
for (size_t j = 0; j < width; ++j)
{
XMVECTOR value = inPixels[j];
XMVECTOR nvalue = XMVector3Transform(value, c_fromP3to2020);
// Convert to ST.2084
nvalue = XMVectorDivide(XMVectorMultiply(nvalue, paperWhite), c_MaxNitsFor2084);
XMFLOAT4A tmp;
XMStoreFloat4A(&tmp, nvalue);
tmp.x = LinearToST2084(tmp.x);
tmp.y = LinearToST2084(tmp.y);
tmp.z = LinearToST2084(tmp.z);
nvalue = XMLoadFloat4A(&tmp);
value = XMVectorSelect(value, nvalue, g_XMSelect1110);
outPixels[j] = value;
}
}, *timage);
break;
case ROTATE_P3_TO_2020:
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[&](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t width, size_t y)
{
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < width; ++j)
{
XMVECTOR value = inPixels[j];
XMVECTOR nvalue = XMVector3Transform(value, c_fromP3to2020);
value = XMVectorSelect(value, nvalue, g_XMSelect1110);
outPixels[j] = value;
}
}, *timage);
break;
default:
hr = E_NOTIMPL;
break;
}
if (FAILED(hr))
{
wprintf(L" FAILED [rotate color apply] (%x)\n", hr);
return 1;
}
auto& tinfo = timage->GetMetadata();
tinfo;
assert(info.width == tinfo.width);
assert(info.height == tinfo.height);
assert(info.depth == tinfo.depth);
assert(info.arraySize == tinfo.arraySize);
assert(info.mipLevels == tinfo.mipLevels);
assert(info.miscFlags == tinfo.miscFlags);
assert(info.format == tinfo.format);
assert(info.dimension == tinfo.dimension);
image.swap(timage);
cimage.reset();
}
// --- Tonemap (if requested) --------------------------------------------------
if (dwOptions & DWORD64(1) << OPT_TONEMAP)
{