Workaround when using WIC for F32 images that exceed 32-bits (#112)

This commit is contained in:
Chuck Walbourn 2018-08-02 12:31:20 -07:00 committed by GitHub
parent 9b61fa93bd
commit ba0280c500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 256 additions and 12 deletions

View File

@ -2627,6 +2627,97 @@ HRESULT DirectX::_ConvertFromR32G32B32A32(
} }
//-------------------------------------------------------------------------------------
// Convert DXGI image to/from GUID_WICPixelFormat64bppRGBAHalf (no range conversions)
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::_ConvertToR16G16B16A16(const Image& srcImage, ScratchImage& image)
{
if (!srcImage.pixels)
return E_POINTER;
HRESULT hr = image.Initialize2D(DXGI_FORMAT_R16G16B16A16_FLOAT, srcImage.width, srcImage.height, 1, 1);
if (FAILED(hr))
return hr;
ScopedAlignedArrayXMVECTOR scanline(static_cast<XMVECTOR*>(_aligned_malloc((sizeof(XMVECTOR) * srcImage.width), 16)));
if (!scanline)
{
image.Release();
return E_OUTOFMEMORY;
}
const Image *img = image.GetImage(0, 0, 0);
if (!img)
{
image.Release();
return E_POINTER;
}
uint8_t* pDest = img->pixels;
if (!pDest)
{
image.Release();
return E_POINTER;
}
const uint8_t *pSrc = srcImage.pixels;
for (size_t h = 0; h < srcImage.height; ++h)
{
if (!_LoadScanline(scanline.get(), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format))
{
image.Release();
return E_FAIL;
}
XMConvertFloatToHalfStream(
reinterpret_cast<HALF*>(pDest), sizeof(HALF),
reinterpret_cast<float*>(scanline.get()), sizeof(float),
srcImage.width * 4);
pSrc += srcImage.rowPitch;
pDest += img->rowPitch;
}
return S_OK;
}
_Use_decl_annotations_
HRESULT DirectX::_ConvertFromR16G16B16A16(const Image& srcImage, const Image& destImage)
{
assert(srcImage.format == DXGI_FORMAT_R16G16B16A16_FLOAT);
if (!srcImage.pixels || !destImage.pixels)
return E_POINTER;
if (srcImage.width != destImage.width || srcImage.height != destImage.height)
return E_FAIL;
ScopedAlignedArrayXMVECTOR scanline(static_cast<XMVECTOR*>(_aligned_malloc((sizeof(XMVECTOR) * srcImage.width), 16)));
if (!scanline)
return E_OUTOFMEMORY;
const uint8_t *pSrc = srcImage.pixels;
uint8_t* pDest = destImage.pixels;
for (size_t h = 0; h < srcImage.height; ++h)
{
XMConvertHalfToFloatStream(
reinterpret_cast<float*>(scanline.get()), sizeof(float),
reinterpret_cast<const HALF*>(pSrc), sizeof(HALF),
srcImage.width * 4);
if (!_StoreScanline(pDest, destImage.rowPitch, destImage.format, scanline.get(), srcImage.width))
return E_FAIL;
pSrc += srcImage.rowPitch;
pDest += destImage.rowPitch;
}
return S_OK;
}
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
// Convert from Linear RGB to sRGB // Convert from Linear RGB to sRGB
// //

View File

@ -84,7 +84,52 @@ namespace
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
// Do conversion, flip/rotate using WIC, conversion cycle // Do conversion, flip/rotate using WIC, conversion cycle
//
// For large images we have to use F16 instead of F32 to avoid exceeding the 32-bit
// memory limitations of WIC.
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
HRESULT PerformFlipRotateViaF16(
const Image& srcImage,
DWORD flags,
const Image& destImage)
{
if (!srcImage.pixels || !destImage.pixels)
return E_POINTER;
assert(srcImage.format != DXGI_FORMAT_R16G16B16A16_FLOAT);
assert(srcImage.format == destImage.format);
ScratchImage temp;
HRESULT hr = _ConvertToR16G16B16A16(srcImage, temp);
if (FAILED(hr))
return hr;
const Image *tsrc = temp.GetImage(0, 0, 0);
if (!tsrc)
return E_POINTER;
ScratchImage rtemp;
hr = rtemp.Initialize2D(DXGI_FORMAT_R16G16B16A16_FLOAT, destImage.width, destImage.height, 1, 1);
if (FAILED(hr))
return hr;
const Image *tdest = rtemp.GetImage(0, 0, 0);
if (!tdest)
return E_POINTER;
hr = PerformFlipRotateUsingWIC(*tsrc, flags, GUID_WICPixelFormat64bppRGBAHalf, *tdest);
if (FAILED(hr))
return hr;
temp.Release();
hr = _ConvertFromR16G16B16A16(*tdest, destImage);
if (FAILED(hr))
return hr;
return S_OK;
}
HRESULT PerformFlipRotateViaF32( HRESULT PerformFlipRotateViaF32(
const Image& srcImage, const Image& srcImage,
DWORD flags, DWORD flags,
@ -206,7 +251,16 @@ HRESULT DirectX::FlipRotate(
else else
{ {
// Case 2: Source format is not supported by WIC, so we have to convert, flip/rotate, and convert back // Case 2: Source format is not supported by WIC, so we have to convert, flip/rotate, and convert back
hr = PerformFlipRotateViaF32(srcImage, flags, *rimage); uint64_t expandedSize = uint64_t(srcImage.width) * uint64_t(srcImage.height) * sizeof(float) * 4;
if (expandedSize > UINT32_MAX)
{
// Image is too large for float32, so have to use float16 instead
hr = PerformFlipRotateViaF16(srcImage, flags, *rimage);
}
else
{
hr = PerformFlipRotateViaF32(srcImage, flags, *rimage);
}
} }
if (FAILED(hr)) if (FAILED(hr))
@ -329,7 +383,16 @@ HRESULT DirectX::FlipRotate(
else else
{ {
// Case 2: Source format is not supported by WIC, so we have to convert, flip/rotate, and convert back // Case 2: Source format is not supported by WIC, so we have to convert, flip/rotate, and convert back
hr = PerformFlipRotateViaF32(src, flags, dst); uint64_t expandedSize = uint64_t(src.width) * uint64_t(src.height) * sizeof(float) * 4;
if (expandedSize > UINT32_MAX)
{
// Image is too large for float32, so have to use float16 instead
hr = PerformFlipRotateViaF16(src, flags, dst);
}
else
{
hr = PerformFlipRotateViaF32(src, flags, dst);
}
} }
if (FAILED(hr)) if (FAILED(hr))

View File

@ -2564,7 +2564,26 @@ HRESULT DirectX::GenerateMipMaps(
static_assert(TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK"); static_assert(TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK");
if (UseWICFiltering(baseImage.format, filter)) bool usewic = UseWICFiltering(baseImage.format, filter);
WICPixelFormatGUID pfGUID = {};
bool wicpf = (usewic) ? _DXGIToWIC(baseImage.format, pfGUID, true) : false;
if (usewic && !wicpf)
{
// Check to see if the source and/or result size is too big for WIC
uint64_t expandedSize = uint64_t(std::max<size_t>(1, baseImage.width >> 1)) * uint64_t(std::max<size_t>(1, baseImage.height >> 1)) * sizeof(float) * 4;
uint64_t expandedSize2 = uint64_t(baseImage.width) * uint64_t(baseImage.height) * sizeof(float) * 4;
if (expandedSize > UINT32_MAX || expandedSize2 > UINT32_MAX)
{
if (filter & TEX_FILTER_FORCE_WIC)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
usewic = false;
}
}
if (usewic)
{ {
//--- Use WIC filtering to generate mipmaps ----------------------------------- //--- Use WIC filtering to generate mipmaps -----------------------------------
switch (filter & TEX_FILTER_MASK) switch (filter & TEX_FILTER_MASK)
@ -2577,8 +2596,7 @@ HRESULT DirectX::GenerateMipMaps(
{ {
static_assert(TEX_FILTER_FANT == TEX_FILTER_BOX, "TEX_FILTER_ flag alias mismatch"); static_assert(TEX_FILTER_FANT == TEX_FILTER_BOX, "TEX_FILTER_ flag alias mismatch");
WICPixelFormatGUID pfGUID; if (wicpf)
if (_DXGIToWIC(baseImage.format, pfGUID, true))
{ {
// Case 1: Base image format is supported by Windows Imaging Component // Case 1: Base image format is supported by Windows Imaging Component
hr = (baseImage.height > 1 || !allow1D) hr = (baseImage.height > 1 || !allow1D)
@ -2754,9 +2772,31 @@ HRESULT DirectX::GenerateMipMaps(
HRESULT hr = E_UNEXPECTED; HRESULT hr = E_UNEXPECTED;
if (baseImages.empty())
return hr;
static_assert(TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK"); static_assert(TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK");
if (!metadata.IsPMAlpha() && UseWICFiltering(metadata.format, filter)) bool usewic = !metadata.IsPMAlpha() && UseWICFiltering(metadata.format, filter);
WICPixelFormatGUID pfGUID = {};
bool wicpf = (usewic) ? _DXGIToWIC(metadata.format, pfGUID, true) : false;
if (usewic && !wicpf)
{
// Check to see if the source and/or result size is too big for WIC
uint64_t expandedSize = uint64_t(std::max<size_t>(1, metadata.width >> 1)) * uint64_t(std::max<size_t>(1, metadata.height >> 1)) * sizeof(float) * 4;
uint64_t expandedSize2 = uint64_t(metadata.width) * uint64_t(metadata.height) * sizeof(float) * 4;
if (expandedSize > UINT32_MAX || expandedSize2 > UINT32_MAX)
{
if (filter & TEX_FILTER_FORCE_WIC)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
usewic = false;
}
}
if (usewic)
{ {
//--- Use WIC filtering to generate mipmaps ----------------------------------- //--- Use WIC filtering to generate mipmaps -----------------------------------
switch (filter & TEX_FILTER_MASK) switch (filter & TEX_FILTER_MASK)
@ -2769,8 +2809,7 @@ HRESULT DirectX::GenerateMipMaps(
{ {
static_assert(TEX_FILTER_FANT == TEX_FILTER_BOX, "TEX_FILTER_ flag alias mismatch"); static_assert(TEX_FILTER_FANT == TEX_FILTER_BOX, "TEX_FILTER_ flag alias mismatch");
WICPixelFormatGUID pfGUID; if (wicpf)
if (_DXGIToWIC(metadata.format, pfGUID, true))
{ {
// Case 1: Base image format is supported by Windows Imaging Component // Case 1: Base image format is supported by Windows Imaging Component
TexMetadata mdata2 = metadata; TexMetadata mdata2 = metadata;

View File

@ -272,6 +272,10 @@ namespace DirectX
_In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata,
_In_ DXGI_FORMAT format, _Out_ ScratchImage& result); _In_ DXGI_FORMAT format, _Out_ ScratchImage& result);
HRESULT __cdecl _ConvertToR16G16B16A16(_In_ const Image& srcImage, _Inout_ ScratchImage& image);
HRESULT __cdecl _ConvertFromR16G16B16A16(_In_ const Image& srcImage, _In_ const Image& destImage);
void __cdecl _ConvertScanline( void __cdecl _ConvertScanline(
_Inout_updates_all_(count) XMVECTOR* pBuffer, _In_ size_t count, _Inout_updates_all_(count) XMVECTOR* pBuffer, _In_ size_t count,
_In_ DXGI_FORMAT outFormat, _In_ DXGI_FORMAT inFormat, _In_ DWORD flags); _In_ DXGI_FORMAT outFormat, _In_ DXGI_FORMAT inFormat, _In_ DWORD flags);

View File

@ -862,6 +862,25 @@ HRESULT DirectX::Resize(
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
} }
bool usewic = UseWICFiltering(srcImage.format, filter);
WICPixelFormatGUID pfGUID = {};
bool wicpf = (usewic) ? _DXGIToWIC(srcImage.format, pfGUID, true) : false;
if (usewic && !wicpf)
{
// Check to see if the source and/or result size is too big for WIC
uint64_t expandedSize = uint64_t(width) * uint64_t(height) * sizeof(float) * 4;
uint64_t expandedSize2 = uint64_t(srcImage.width) * uint64_t(srcImage.height) * sizeof(float) * 4;
if (expandedSize > UINT32_MAX || expandedSize2 > UINT32_MAX)
{
if (filter & TEX_FILTER_FORCE_WIC)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
usewic = false;
}
}
HRESULT hr = image.Initialize2D(srcImage.format, width, height, 1, 1); HRESULT hr = image.Initialize2D(srcImage.format, width, height, 1, 1);
if (FAILED(hr)) if (FAILED(hr))
return hr; return hr;
@ -870,10 +889,9 @@ HRESULT DirectX::Resize(
if (!rimage) if (!rimage)
return E_POINTER; return E_POINTER;
if (UseWICFiltering(srcImage.format, filter)) if (usewic)
{ {
WICPixelFormatGUID pfGUID; if (wicpf)
if (_DXGIToWIC(srcImage.format, pfGUID, true))
{ {
// Case 1: Source format is supported by Windows Imaging Component // Case 1: Source format is supported by Windows Imaging Component
hr = PerformResizeUsingWIC(srcImage, filter, pfGUID, *rimage); hr = PerformResizeUsingWIC(srcImage, filter, pfGUID, *rimage);
@ -886,6 +904,7 @@ HRESULT DirectX::Resize(
} }
else else
{ {
// Case 3: not using WIC resizing
hr = PerformResizeUsingCustomFilters(srcImage, filter, *rimage); hr = PerformResizeUsingCustomFilters(srcImage, filter, *rimage);
} }
@ -931,6 +950,20 @@ HRESULT DirectX::Resize(
WICPixelFormatGUID pfGUID = {}; WICPixelFormatGUID pfGUID = {};
bool wicpf = (usewic) ? _DXGIToWIC(metadata.format, pfGUID, true) : false; bool wicpf = (usewic) ? _DXGIToWIC(metadata.format, pfGUID, true) : false;
if (usewic && !wicpf)
{
// Check to see if the source and/or result size is too big for WIC
uint64_t expandedSize = uint64_t(width) * uint64_t(height) * sizeof(float) * 4;
uint64_t expandedSize2 = uint64_t(metadata.width) * uint64_t(metadata.height) * sizeof(float) * 4;
if (expandedSize > UINT32_MAX || expandedSize2 > UINT32_MAX)
{
if (filter & TEX_FILTER_FORCE_WIC)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
usewic = false;
}
}
switch (metadata.dimension) switch (metadata.dimension)
{ {
case TEX_DIMENSION_TEXTURE1D: case TEX_DIMENSION_TEXTURE1D:

View File

@ -80,6 +80,7 @@ enum OPTIONS
OPT_USE_DX10, OPT_USE_DX10,
OPT_NOLOGO, OPT_NOLOGO,
OPT_SEPALPHA, OPT_SEPALPHA,
OPT_NO_WIC,
OPT_DEMUL_ALPHA, OPT_DEMUL_ALPHA,
OPT_TA_WRAP, OPT_TA_WRAP,
OPT_TA_MIRROR, OPT_TA_MIRROR,
@ -136,6 +137,7 @@ const SValue g_pOptions [] =
{ L"dx10", OPT_USE_DX10 }, { L"dx10", OPT_USE_DX10 },
{ L"nologo", OPT_NOLOGO }, { L"nologo", OPT_NOLOGO },
{ L"sepalpha", OPT_SEPALPHA }, { L"sepalpha", OPT_SEPALPHA },
{ L"nowic", OPT_NO_WIC },
{ L"alpha", OPT_DEMUL_ALPHA }, { L"alpha", OPT_DEMUL_ALPHA },
{ L"wrap", OPT_TA_WRAP }, { L"wrap", OPT_TA_WRAP },
{ L"mirror", OPT_TA_MIRROR }, { L"mirror", OPT_TA_MIRROR },
@ -519,6 +521,7 @@ namespace
wprintf(L" -o <filename> output filename\n"); wprintf(L" -o <filename> output filename\n");
wprintf(L" -y overwrite existing output file (if any)\n"); wprintf(L" -y overwrite existing output file (if any)\n");
wprintf(L" -sepalpha resize alpha channel separately from color channels\n"); wprintf(L" -sepalpha resize alpha channel separately from color channels\n");
wprintf(L" -nowic Force non-WIC filtering\n");
wprintf(L" -wrap, -mirror texture addressing mode (wrap, mirror, or clamp)\n"); wprintf(L" -wrap, -mirror texture addressing mode (wrap, mirror, or clamp)\n");
wprintf(L" -alpha convert premultiplied alpha to straight alpha\n"); wprintf(L" -alpha convert premultiplied alpha to straight alpha\n");
wprintf(L" -dx10 Force use of 'DX10' extended header\n"); wprintf(L" -dx10 Force use of 'DX10' extended header\n");
@ -1067,6 +1070,10 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
dwFilterOpts |= TEX_FILTER_SEPARATE_ALPHA; dwFilterOpts |= TEX_FILTER_SEPARATE_ALPHA;
break; break;
case OPT_NO_WIC:
dwFilterOpts |= TEX_FILTER_FORCE_NON_WIC;
break;
case OPT_OUTPUTFILE: case OPT_OUTPUTFILE:
{ {
wcscpy_s(szOutputFile, MAX_PATH, pValue); wcscpy_s(szOutputFile, MAX_PATH, pValue);

View File

@ -75,6 +75,7 @@ enum OPTIONS
OPT_NOLOGO, OPT_NOLOGO,
OPT_TIMING, OPT_TIMING,
OPT_SEPALPHA, OPT_SEPALPHA,
OPT_NO_WIC,
OPT_TYPELESS_UNORM, OPT_TYPELESS_UNORM,
OPT_TYPELESS_FLOAT, OPT_TYPELESS_FLOAT,
OPT_PREMUL_ALPHA, OPT_PREMUL_ALPHA,
@ -159,6 +160,7 @@ const SValue g_pOptions[] =
{ L"nologo", OPT_NOLOGO }, { L"nologo", OPT_NOLOGO },
{ L"timing", OPT_TIMING }, { L"timing", OPT_TIMING },
{ L"sepalpha", OPT_SEPALPHA }, { L"sepalpha", OPT_SEPALPHA },
{ L"nowic", OPT_NO_WIC },
{ L"tu", OPT_TYPELESS_UNORM }, { L"tu", OPT_TYPELESS_UNORM },
{ L"tf", OPT_TYPELESS_FLOAT }, { L"tf", OPT_TYPELESS_FLOAT },
{ L"pmalpha", OPT_PREMUL_ALPHA }, { L"pmalpha", OPT_PREMUL_ALPHA },
@ -707,6 +709,7 @@ namespace
wprintf(L" -vflip vertical flip of source image\n"); wprintf(L" -vflip vertical flip of source image\n");
wprintf(L" -sepalpha resize/generate mips alpha channel separately\n"); wprintf(L" -sepalpha resize/generate mips alpha channel separately\n");
wprintf(L" from color channels\n"); wprintf(L" from color channels\n");
wprintf(L" -nowic Force non-WIC filtering\n");
wprintf(L" -wrap, -mirror texture addressing mode (wrap, mirror, or clamp)\n"); wprintf(L" -wrap, -mirror texture addressing mode (wrap, mirror, or clamp)\n");
wprintf(L" -pmalpha convert final texture to use premultiplied alpha\n"); wprintf(L" -pmalpha convert final texture to use premultiplied alpha\n");
wprintf(L" -alpha convert premultiplied alpha to straight alpha\n"); wprintf(L" -alpha convert premultiplied alpha to straight alpha\n");
@ -743,7 +746,7 @@ namespace
L" (defaults to 1.0)\n"); L" (defaults to 1.0)\n");
wprintf(L" -c <hex-RGB> colorkey (a.k.a. chromakey) transparency\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" -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" -nits <value> paper-white value in nits to use for HDR10 (def: 200.0)\n");
wprintf(L" -tonemap Apply a tonemap operator based on maximum luminance\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" -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"); wprintf(L" -flist <filename> use text file with a list of input files (one per line)\n");
@ -1276,6 +1279,10 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
dwFilterOpts |= TEX_FILTER_SEPARATE_ALPHA; dwFilterOpts |= TEX_FILTER_SEPARATE_ALPHA;
break; break;
case OPT_NO_WIC:
dwFilterOpts |= TEX_FILTER_FORCE_NON_WIC;
break;
case OPT_PREFIX: case OPT_PREFIX:
wcscpy_s(szPrefix, MAX_PATH, pValue); wcscpy_s(szPrefix, MAX_PATH, pValue);
break; break;