From 5b420edd7a0d2eee0457e5f64e0b05146af87944 Mon Sep 17 00:00:00 2001 From: walbourn Date: Sat, 22 Oct 2022 14:35:26 -0700 Subject: [PATCH] Add EXR source in Auxiliary directory --- Auxiliary/DirectXTexEXR.cpp | 596 ++++++++++++++++++++++++++++++++++++ Auxiliary/DirectXTexEXR.h | 26 ++ CMakeLists.txt | 6 +- CMakePresets.json | 10 + README.md | 4 + 5 files changed, 639 insertions(+), 3 deletions(-) create mode 100644 Auxiliary/DirectXTexEXR.cpp create mode 100644 Auxiliary/DirectXTexEXR.h diff --git a/Auxiliary/DirectXTexEXR.cpp b/Auxiliary/DirectXTexEXR.cpp new file mode 100644 index 0000000..53cbdca --- /dev/null +++ b/Auxiliary/DirectXTexEXR.cpp @@ -0,0 +1,596 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXTexEXR.cpp +// +// DirectXTex Auxillary functions for using the OpenEXR library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- + +#include "DirectXTexP.h" + +#include "DirectXTexEXR.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +// +// Requires the OpenEXR library and ZLIB +// + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wswitch-enum" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wimplicit-int-conversion" +#pragma clang diagnostic ignored "-Wlanguage-extension-token" +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#pragma clang diagnostic ignored "-Wshadow-field-in-constructor" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + +#pragma warning(push) +#pragma warning(disable : 4244 4996) +#include +#include +#pragma warning(pop) + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +static_assert(sizeof(Imf::Rgba) == 8, "Mismatch size"); + +using namespace DirectX; +using PackedVector::XMHALF4; + +#ifdef _WIN32 +namespace +{ + class com_exception : public std::exception + { + public: + com_exception(HRESULT hr) noexcept : result(hr) {} + + const char* what() const noexcept override + { + static char s_str[64] = {}; + sprintf_s(s_str, "Failure with HRESULT of %08X", static_cast(result)); + return s_str; + } + + HRESULT get_result() const noexcept { return result; } + + private: + HRESULT result; + }; + + class InputStream : public Imf::IStream + { + public: + InputStream(HANDLE hFile, const char fileName[]) : + IStream(fileName), m_hFile(hFile) + { + const LARGE_INTEGER dist = {}; + LARGE_INTEGER result; + if (!SetFilePointerEx(m_hFile, dist, &result, FILE_END)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + + m_EOF = result.QuadPart; + + if (!SetFilePointerEx(m_hFile, dist, nullptr, FILE_BEGIN)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + } + + InputStream(const InputStream&) = delete; + InputStream& operator = (const InputStream&) = delete; + + InputStream(InputStream&&) = delete; + InputStream& operator=(InputStream&&) = delete; + + bool read(char c[], int n) override + { + DWORD bytesRead; + if (!ReadFile(m_hFile, c, static_cast(n), &bytesRead, nullptr)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + + const LARGE_INTEGER dist = {}; + LARGE_INTEGER result; + if (!SetFilePointerEx(m_hFile, dist, &result, FILE_CURRENT)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + + return result.QuadPart >= m_EOF; + } + + uint64_t tellg() override + { + const LARGE_INTEGER dist = {}; + LARGE_INTEGER result; + if (!SetFilePointerEx(m_hFile, dist, &result, FILE_CURRENT)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + return static_cast(result.QuadPart); + } + + void seekg(uint64_t pos) override + { + LARGE_INTEGER dist; + dist.QuadPart = static_cast(pos); + if (!SetFilePointerEx(m_hFile, dist, nullptr, FILE_BEGIN)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + } + + void clear() override + { + SetLastError(0); + } + + private: + HANDLE m_hFile; + LONGLONG m_EOF; + }; + + class OutputStream : public Imf::OStream + { + public: + OutputStream(HANDLE hFile, const char fileName[]) : + OStream(fileName), m_hFile(hFile) {} + + OutputStream(const OutputStream&) = delete; + OutputStream& operator = (const OutputStream&) = delete; + + OutputStream(OutputStream&&) = delete; + OutputStream& operator=(OutputStream&&) = delete; + + void write(const char c[], int n) override + { + DWORD bytesWritten; + if (!WriteFile(m_hFile, c, static_cast(n), &bytesWritten, nullptr)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + } + + uint64_t tellp() override + { + const LARGE_INTEGER dist = {}; + LARGE_INTEGER result; + if (!SetFilePointerEx(m_hFile, dist, &result, FILE_CURRENT)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + return static_cast(result.QuadPart); + } + + void seekp(uint64_t pos) override + { + LARGE_INTEGER dist; + dist.QuadPart = static_cast(pos); + if (!SetFilePointerEx(m_hFile, dist, nullptr, FILE_BEGIN)) + { + throw com_exception(HRESULT_FROM_WIN32(GetLastError())); + } + } + + private: + HANDLE m_hFile; + }; +} +#endif // _WIN32 + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Obtain metadata from EXR file on disk +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::GetMetadataFromEXRFile(const wchar_t* szFile, TexMetadata& metadata) +{ + if (!szFile) + return E_INVALIDARG; + +#ifdef _WIN32 + char fileName[MAX_PATH] = {}; + const int result = WideCharToMultiByte(CP_UTF8, 0, szFile, -1, fileName, MAX_PATH, nullptr, nullptr); + if (result <= 0) + { + *fileName = 0; + } + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile(safe_handle(CreateFile2( + szFile, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + nullptr))); +#else + ScopedHandle hFile(safe_handle(CreateFileW( + szFile, + GENERIC_READ, FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, + nullptr))); +#endif + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + InputStream stream(hFile.get(), fileName); +#else + std::wstring wFileName(szFile); + std::string fileName(wFileName.cbegin(), wFileName.cend()); +#endif + + HRESULT hr = S_OK; + + try + { +#ifdef _WIN32 + Imf::RgbaInputFile file(stream); +#else + Imf::RgbaInputFile file(fileName.c_str()); +#endif + + const auto dw = file.dataWindow(); + + const int width = dw.max.x - dw.min.x + 1; + const int height = dw.max.y - dw.min.y + 1; + + if (width < 1 || height < 1) + return E_FAIL; + + metadata.width = static_cast(width); + metadata.height = static_cast(height); + metadata.depth = metadata.arraySize = metadata.mipLevels = 1; + metadata.format = DXGI_FORMAT_R16G16B16A16_FLOAT; + metadata.dimension = TEX_DIMENSION_TEXTURE2D; + } +#ifdef _WIN32 + catch (const com_exception& exc) + { +#ifdef _DEBUG + OutputDebugStringA(exc.what()); +#endif + hr = exc.get_result(); + } +#endif +#if defined(_WIN32) && defined(_DEBUG) + catch (const std::exception& exc) + { + OutputDebugStringA(exc.what()); + hr = E_FAIL; + } +#else + catch (const std::exception&) + { + hr = E_FAIL; + } +#endif + catch (...) + { + hr = E_UNEXPECTED; + } + + return hr; +} + + +//------------------------------------------------------------------------------------- +// Load a EXR file from disk +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadFromEXRFile(const wchar_t* szFile, TexMetadata* metadata, ScratchImage& image) +{ + if (!szFile) + return E_INVALIDARG; + + image.Release(); + + if (metadata) + { + memset(metadata, 0, sizeof(TexMetadata)); + } + +#ifdef _WIN32 + char fileName[MAX_PATH] = {}; + const int result = WideCharToMultiByte(CP_UTF8, 0, szFile, -1, fileName, MAX_PATH, nullptr, nullptr); + if (result <= 0) + { + *fileName = 0; + } + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile(safe_handle(CreateFile2( + szFile, + GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, + nullptr))); +#else + ScopedHandle hFile(safe_handle(CreateFileW( + szFile, + GENERIC_READ, FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, + nullptr))); +#endif + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + InputStream stream(hFile.get(), fileName); +#else + std::wstring wFileName(szFile); + std::string fileName(wFileName.cbegin(), wFileName.cend()); +#endif + + HRESULT hr = S_OK; + + try + { +#ifdef _WIN32 + Imf::RgbaInputFile file(stream); +#else + Imf::RgbaInputFile file(fileName.c_str()); +#endif + + auto const dw = file.dataWindow(); + + const int width = dw.max.x - dw.min.x + 1; + const int height = dw.max.y - dw.min.y + 1; + + if (width < 1 || height < 1) + return E_FAIL; + + if (metadata) + { + metadata->width = static_cast(width); + metadata->height = static_cast(height); + metadata->depth = metadata->arraySize = metadata->mipLevels = 1; + metadata->format = DXGI_FORMAT_R16G16B16A16_FLOAT; + metadata->dimension = TEX_DIMENSION_TEXTURE2D; + } + + hr = image.Initialize2D(DXGI_FORMAT_R16G16B16A16_FLOAT, + static_cast(width), static_cast(height), 1u, 1u); + if (FAILED(hr)) + return hr; + + file.setFrameBuffer(reinterpret_cast(image.GetPixels()) - dw.min.x - dw.min.y * width, 1, static_cast(width)); + file.readPixels(dw.min.y, dw.max.y); + } +#ifdef _WIN32 + catch (const com_exception& exc) + { +#ifdef _DEBUG + OutputDebugStringA(exc.what()); +#endif + hr = exc.get_result(); + } +#endif +#if defined(_WIN32) && defined(_DEBUG) + catch (const std::exception& exc) + { + OutputDebugStringA(exc.what()); + hr = E_FAIL; + } +#else + catch (const std::exception&) + { + hr = E_FAIL; + } +#endif + catch (...) + { + hr = E_UNEXPECTED; + } + + if (FAILED(hr)) + { + image.Release(); + } + + return hr; +} + + +//------------------------------------------------------------------------------------- +// Save a EXR file to disk +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::SaveToEXRFile(const Image& image, const wchar_t* szFile) +{ + if (!szFile) + return E_INVALIDARG; + + if (!image.pixels) + return E_POINTER; + + if (image.width > INT32_MAX || image.height > INT32_MAX) + return /* HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) */ static_cast(0x80070032L); + + switch (image.format) + { + case DXGI_FORMAT_R16G16B16A16_FLOAT: + if ((image.rowPitch % 8) > 0) + return E_FAIL; + break; + + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32_FLOAT: + break; + + default: + return /* HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) */ static_cast(0x80070032L); + } + +#ifdef _WIN32 + char fileName[MAX_PATH] = {}; + const int result = WideCharToMultiByte(CP_UTF8, 0, szFile, -1, fileName, MAX_PATH, nullptr, nullptr); + if (result <= 0) + { + *fileName = 0; + } + // Create file and write header +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile(safe_handle(CreateFile2( + szFile, + GENERIC_WRITE, 0, CREATE_ALWAYS, + nullptr))); +#else + ScopedHandle hFile(safe_handle(CreateFileW( + szFile, + GENERIC_WRITE, 0, + nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + nullptr))); +#endif + if (!hFile) + { + return HRESULT_FROM_WIN32(GetLastError()); + } + + auto_delete_file delonfail(hFile.get()); + + OutputStream stream(hFile.get(), fileName); +#else + std::wstring wFileName(szFile); + std::string fileName(wFileName.cbegin(), wFileName.cend()); +#endif + + HRESULT hr = S_OK; + + try + { + const int width = static_cast(image.width); + const int height = static_cast(image.height); + +#ifdef _WIN32 + Imf::RgbaOutputFile file(stream, Imf::Header(width, height), Imf::WRITE_RGBA); +#else + Imf::RgbaOutputFile file(fileName.c_str(), Imf::Header(width, height), Imf::WRITE_RGBA); +#endif + + if (image.format == DXGI_FORMAT_R16G16B16A16_FLOAT) + { + file.setFrameBuffer(reinterpret_cast(image.pixels), 1, image.rowPitch / 8); + file.writePixels(height); + } + else + { + const uint64_t bytes = image.width * image.height; + + if (bytes > UINT32_MAX) + { + return /* HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW) */ static_cast(0x80070216L); + } + + std::unique_ptr temp(new (std::nothrow) XMHALF4[static_cast(bytes)]); + if (!temp) + return E_OUTOFMEMORY; + + file.setFrameBuffer(reinterpret_cast(temp.get()), 1, image.width); + + auto sPtr = image.pixels; + auto dPtr = temp.get(); + if (image.format == DXGI_FORMAT_R32G32B32A32_FLOAT) + { + for (int j = 0; j < height; ++j) + { + auto srcPtr = reinterpret_cast(sPtr); + auto destPtr = dPtr; + for (int k = 0; k < width; ++k, ++srcPtr, ++destPtr) + { + const XMVECTOR v = XMLoadFloat4(srcPtr); + PackedVector::XMStoreHalf4(destPtr, v); + } + + sPtr += image.rowPitch; + dPtr += width; + + file.writePixels(1); + } + } + else + { + assert(image.format == DXGI_FORMAT_R32G32B32_FLOAT); + + for (int j = 0; j < height; ++j) + { + auto srcPtr = reinterpret_cast(sPtr); + auto destPtr = dPtr; + for (int k = 0; k < width; ++k, ++srcPtr, ++destPtr) + { + XMVECTOR v = XMLoadFloat3(srcPtr); + v = XMVectorSelect(g_XMIdentityR3, v, g_XMSelect1110); + PackedVector::XMStoreHalf4(destPtr, v); + } + + sPtr += image.rowPitch; + dPtr += width; + + file.writePixels(1); + } + } + } + } +#ifdef _WIN32 + catch (const com_exception& exc) + { +#ifdef _DEBUG + OutputDebugStringA(exc.what()); +#endif + hr = exc.get_result(); + } +#endif +#if defined(_WIN32) && defined(_DEBUG) + catch (const std::exception& exc) + { + OutputDebugStringA(exc.what()); + hr = E_FAIL; + } +#else + catch (const std::exception&) + { + hr = E_FAIL; + } +#endif + catch (...) + { + hr = E_UNEXPECTED; + } + + if (FAILED(hr)) + return hr; + +#ifdef _WIN32 + delonfail.clear(); +#endif + + return S_OK; +} diff --git a/Auxiliary/DirectXTexEXR.h b/Auxiliary/DirectXTexEXR.h new file mode 100644 index 0000000..bec66e8 --- /dev/null +++ b/Auxiliary/DirectXTexEXR.h @@ -0,0 +1,26 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXTexEXR.h +// +// DirectXTex Auxillary functions for using the OpenEXR library +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "DirectXTex.h" + + +namespace DirectX +{ + HRESULT __cdecl GetMetadataFromEXRFile( + _In_z_ const wchar_t* szFile, + _Out_ TexMetadata& metadata); + + HRESULT __cdecl LoadFromEXRFile( + _In_z_ const wchar_t* szFile, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image); + + HRESULT __cdecl SaveToEXRFile(_In_ const Image& image, _In_z_ const wchar_t* szFile); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 7fa04f0..f79fe77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,6 @@ option(ENABLE_CODE_ANALYSIS "Use Static Code Analysis on build" OFF) option(USE_PREBUILT_SHADERS "Use externally built HLSL shaders" OFF) # Includes the functions for loading/saving OpenEXR files at runtime -# NOTE requires adding DirectXTexEXR.h/.cpp source files (vcpkg does this automatically) option(ENABLE_OPENEXR_SUPPORT "Build with OpenEXR support" OFF) set(CMAKE_CXX_STANDARD 17) @@ -107,10 +106,10 @@ endif() if(ENABLE_OPENEXR_SUPPORT) set(LIBRARY_HEADERS ${LIBRARY_HEADERS} - DirectXTex/DirectXTexEXR.h) + Auxiliary/DirectXTexEXR.h) set(LIBRARY_SOURCES ${LIBRARY_SOURCES} - DirectXTex/DirectXTexEXR.cpp) + Auxiliary/DirectXTexEXR.cpp) endif() if(BUILD_DX11 AND WIN32) @@ -242,6 +241,7 @@ if(BUILD_TOOLS AND WIN32 AND (NOT WINDOWS_STORE)) if(ENABLE_OPENEXR_SUPPORT) foreach(t IN LISTS TOOL_EXES) + target_include_directories(${t} PRIVATE Auxiliary) target_link_libraries(${t} ${OPENEXR_ILMIMF_LIBRARY}) target_compile_options(${t} PRIVATE -DUSE_OPENEXR) endforeach() diff --git a/CMakePresets.json b/CMakePresets.json index 1af268a..5d42a13 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -119,6 +119,13 @@ }, "hidden": true }, + { + "name": "EXR", + "cacheVariables": { + "ENABLE_OPENEXR_SUPPORT": true + }, + "hidden": true + }, { "name": "MinGW32", "hidden": true, @@ -170,6 +177,9 @@ { "name": "arm64-Debug-VCPKG" , "description": "MSVC for ARM64 (Debug) using VCPKG", "inherits": [ "base", "ARM64", "Debug", "MSVC", "VCPKG" ] }, { "name": "arm64-Release-VCPKG", "description": "MSVC for ARM64 (Release) using VCPKG", "inherits": [ "base", "ARM64", "Release", "MSVC", "VCPKG" ] }, + { "name": "x64-Debug-EXR" , "description": "MSVC for x64 (Debug) using VCPKG/OpenEXR", "inherits": [ "base", "x64", "Debug", "MSVC", "VCPKG", "EXR" ] }, + { "name": "x64-Release-EXR" , "description": "MSVC for x64 (Release) using VCPKG/OpenEXR", "inherits": [ "base", "x64", "Release", "MSVC", "VCPKG", "EXR" ] }, + { "name": "x64-Debug-Clang" , "description": "Clang/LLVM for x64 (Debug) with DX12", "inherits": [ "base", "x64", "Debug", "Clang" ] }, { "name": "x64-Release-Clang" , "description": "Clang/LLVM for x64 (Release) with DX12", "inherits": [ "base", "x64", "Release", "Clang" ] }, { "name": "x86-Debug-Clang" , "description": "Clang/LLVM for x86 (Debug) with DX12", "inherits": [ "base", "x86", "Debug", "Clang" ], "environment": { "CXXFLAGS": "-m32" } }, diff --git a/README.md b/README.md index b8ba3dc..690067a 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ These components are designed to work without requiring any content from the leg > The majority of the header files here are intended for internal implementation of the library only (``BC.h``, ``BCDirectCompute.h``, ``DDS.h``, ``DirectXTexP.h``, etc.). Only ``DirectXTex.h`` and ``DirectXTex.inl`` are meant as the 'public' header for the library. +* ``Auxiliary\`` + + + Contains optional source files for the DirectXTex library, such as adapter loading functions using the OpenEXR library. + * ``Texconv\`` + This DirectXTex sample is an implementation of the [texconv](https://github.com/Microsoft/DirectXTex/wiki/Texconv) command-line texture utility from the DirectX SDK utilizing DirectXTex rather than D3DX.