diff --git a/DirectXTex/DirectXTexConvert.cpp b/DirectXTex/DirectXTexConvert.cpp index 41858d5..d9c2485 100644 --- a/DirectXTex/DirectXTexConvert.cpp +++ b/DirectXTex/DirectXTexConvert.cpp @@ -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(_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(pDest), sizeof(HALF), + reinterpret_cast(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(_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(scanline.get()), sizeof(float), + reinterpret_cast(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 // diff --git a/DirectXTex/DirectXTexFlipRotate.cpp b/DirectXTex/DirectXTexFlipRotate.cpp index 8870dbf..f284352 100644 --- a/DirectXTex/DirectXTexFlipRotate.cpp +++ b/DirectXTex/DirectXTexFlipRotate.cpp @@ -84,7 +84,52 @@ namespace //------------------------------------------------------------------------------------- // 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( const Image& srcImage, DWORD flags, @@ -206,7 +251,16 @@ HRESULT DirectX::FlipRotate( else { // 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)) @@ -329,7 +383,16 @@ HRESULT DirectX::FlipRotate( else { // 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)) diff --git a/DirectXTex/DirectXTexMipmaps.cpp b/DirectXTex/DirectXTexMipmaps.cpp index ac37679..7cda4bf 100644 --- a/DirectXTex/DirectXTexMipmaps.cpp +++ b/DirectXTex/DirectXTexMipmaps.cpp @@ -2564,7 +2564,26 @@ HRESULT DirectX::GenerateMipMaps( 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(1, baseImage.width >> 1)) * uint64_t(std::max(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 ----------------------------------- 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"); - WICPixelFormatGUID pfGUID; - if (_DXGIToWIC(baseImage.format, pfGUID, true)) + if (wicpf) { // Case 1: Base image format is supported by Windows Imaging Component hr = (baseImage.height > 1 || !allow1D) @@ -2754,9 +2772,31 @@ HRESULT DirectX::GenerateMipMaps( 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"); - 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(1, metadata.width >> 1)) * uint64_t(std::max(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 ----------------------------------- 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"); - WICPixelFormatGUID pfGUID; - if (_DXGIToWIC(metadata.format, pfGUID, true)) + if (wicpf) { // Case 1: Base image format is supported by Windows Imaging Component TexMetadata mdata2 = metadata; diff --git a/DirectXTex/DirectXTexP.h b/DirectXTex/DirectXTexP.h index 19c6045..76aa086 100644 --- a/DirectXTex/DirectXTexP.h +++ b/DirectXTex/DirectXTexP.h @@ -272,6 +272,10 @@ namespace DirectX _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _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( _Inout_updates_all_(count) XMVECTOR* pBuffer, _In_ size_t count, _In_ DXGI_FORMAT outFormat, _In_ DXGI_FORMAT inFormat, _In_ DWORD flags); diff --git a/DirectXTex/DirectXTexResize.cpp b/DirectXTex/DirectXTexResize.cpp index 5988a6a..49b3824 100644 --- a/DirectXTex/DirectXTexResize.cpp +++ b/DirectXTex/DirectXTexResize.cpp @@ -862,6 +862,25 @@ HRESULT DirectX::Resize( 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); if (FAILED(hr)) return hr; @@ -870,10 +889,9 @@ HRESULT DirectX::Resize( if (!rimage) return E_POINTER; - if (UseWICFiltering(srcImage.format, filter)) + if (usewic) { - WICPixelFormatGUID pfGUID; - if (_DXGIToWIC(srcImage.format, pfGUID, true)) + if (wicpf) { // Case 1: Source format is supported by Windows Imaging Component hr = PerformResizeUsingWIC(srcImage, filter, pfGUID, *rimage); @@ -886,6 +904,7 @@ HRESULT DirectX::Resize( } else { + // Case 3: not using WIC resizing hr = PerformResizeUsingCustomFilters(srcImage, filter, *rimage); } @@ -931,6 +950,20 @@ HRESULT DirectX::Resize( 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(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) { case TEX_DIMENSION_TEXTURE1D: diff --git a/Texassemble/texassemble.cpp b/Texassemble/texassemble.cpp index c9c17c1..a7bf7f1 100644 --- a/Texassemble/texassemble.cpp +++ b/Texassemble/texassemble.cpp @@ -80,6 +80,7 @@ enum OPTIONS OPT_USE_DX10, OPT_NOLOGO, OPT_SEPALPHA, + OPT_NO_WIC, OPT_DEMUL_ALPHA, OPT_TA_WRAP, OPT_TA_MIRROR, @@ -136,6 +137,7 @@ const SValue g_pOptions [] = { L"dx10", OPT_USE_DX10 }, { L"nologo", OPT_NOLOGO }, { L"sepalpha", OPT_SEPALPHA }, + { L"nowic", OPT_NO_WIC }, { L"alpha", OPT_DEMUL_ALPHA }, { L"wrap", OPT_TA_WRAP }, { L"mirror", OPT_TA_MIRROR }, @@ -519,6 +521,7 @@ namespace wprintf(L" -o output filename\n"); wprintf(L" -y overwrite existing output file (if any)\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" -alpha convert premultiplied alpha to straight alpha\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; break; + case OPT_NO_WIC: + dwFilterOpts |= TEX_FILTER_FORCE_NON_WIC; + break; + case OPT_OUTPUTFILE: { wcscpy_s(szOutputFile, MAX_PATH, pValue); diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index cf3fb50..0c83595 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -75,6 +75,7 @@ enum OPTIONS OPT_NOLOGO, OPT_TIMING, OPT_SEPALPHA, + OPT_NO_WIC, OPT_TYPELESS_UNORM, OPT_TYPELESS_FLOAT, OPT_PREMUL_ALPHA, @@ -159,6 +160,7 @@ const SValue g_pOptions[] = { L"nologo", OPT_NOLOGO }, { L"timing", OPT_TIMING }, { L"sepalpha", OPT_SEPALPHA }, + { L"nowic", OPT_NO_WIC }, { L"tu", OPT_TYPELESS_UNORM }, { L"tf", OPT_TYPELESS_FLOAT }, { L"pmalpha", OPT_PREMUL_ALPHA }, @@ -707,6 +709,7 @@ namespace wprintf(L" -vflip vertical flip of source image\n"); wprintf(L" -sepalpha resize/generate mips alpha channel separately\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" -pmalpha convert final texture to use premultiplied alpha\n"); wprintf(L" -alpha convert premultiplied alpha to straight alpha\n"); @@ -743,7 +746,7 @@ namespace L" (defaults to 1.0)\n"); wprintf(L" -c colorkey (a.k.a. chromakey) transparency\n"); wprintf(L" -rotatecolor rotates color primaries and/or applies a curve\n"); - wprintf(L" -nits paper-white value in nits to use for HDR10 (defaults to 200.0)\n"); + wprintf(L" -nits 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" -x2bias Enable *2 - 1 conversion cases for unorm/pos-only-float\n"); wprintf(L" -flist 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; break; + case OPT_NO_WIC: + dwFilterOpts |= TEX_FILTER_FORCE_NON_WIC; + break; + case OPT_PREFIX: wcscpy_s(szPrefix, MAX_PATH, pValue); break;