diff --git a/Auxiliary/DirectXTexEXR.cpp b/Auxiliary/DirectXTexEXR.cpp index 94070a4d..b34ca86f 100644 --- a/Auxiliary/DirectXTexEXR.cpp +++ b/Auxiliary/DirectXTexEXR.cpp @@ -82,10 +82,10 @@ namespace HRESULT result; }; - class InputStream : public Imf::IStream + class EXRInputStream : public Imf::IStream { public: - InputStream(HANDLE hFile, const char fileName[]) : + EXRInputStream(HANDLE hFile, const char fileName[]) : IStream(fileName), m_hFile(hFile) { const LARGE_INTEGER dist = {}; @@ -103,11 +103,11 @@ namespace } } - InputStream(const InputStream&) = delete; - InputStream& operator = (const InputStream&) = delete; + EXRInputStream(const EXRInputStream&) = delete; + EXRInputStream& operator = (const EXRInputStream&) = delete; - InputStream(InputStream&&) = delete; - InputStream& operator=(InputStream&&) = delete; + EXRInputStream(EXRInputStream&&) = delete; + EXRInputStream& operator=(EXRInputStream&&) = delete; bool read(char c[], int n) override { @@ -165,18 +165,18 @@ namespace LONGLONG m_EOF; }; - class OutputStream : public Imf::OStream + class EXROutputStream : public Imf::OStream { public: - OutputStream(HANDLE hFile, const char fileName[]) : + EXROutputStream(HANDLE hFile, const char fileName[]) : OStream(fileName), m_hFile(hFile) {} - OutputStream(const OutputStream&) = delete; - OutputStream& operator = (const OutputStream&) = delete; + EXROutputStream(const EXROutputStream&) = delete; + EXROutputStream& operator = (const EXROutputStream&) = delete; - OutputStream(OutputStream&&) = delete; - OutputStream& operator=(OutputStream&&) = delete; + EXROutputStream(EXROutputStream&&) = delete; + EXROutputStream& operator=(EXROutputStream&&) = delete; void write(const char c[], int n) override { @@ -250,7 +250,7 @@ HRESULT DirectX::GetMetadataFromEXRFile(const wchar_t* szFile, TexMetadata& meta return HRESULT_FROM_WIN32(GetLastError()); } - InputStream stream(hFile.get(), fileName.c_str()); + EXRInputStream stream(hFile.get(), fileName.c_str()); #else std::wstring wFileName(szFile); std::string fileName(wFileName.cbegin(), wFileName.cend()); @@ -359,7 +359,7 @@ HRESULT DirectX::LoadFromEXRFile(const wchar_t* szFile, TexMetadata* metadata, S return HRESULT_FROM_WIN32(GetLastError()); } - InputStream stream(hFile.get(), fileName.c_str()); + EXRInputStream stream(hFile.get(), fileName.c_str()); #else std::wstring wFileName(szFile); std::string fileName(wFileName.cbegin(), wFileName.cend()); @@ -502,7 +502,7 @@ HRESULT DirectX::SaveToEXRFile(const Image& image, const wchar_t* szFile) auto_delete_file delonfail(hFile.get()); - OutputStream stream(hFile.get(), fileName.c_str()); + EXROutputStream stream(hFile.get(), fileName.c_str()); #else std::wstring wFileName(szFile); std::string fileName(wFileName.cbegin(), wFileName.cend()); diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index b3e4a7a4..21d2c3c2 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -576,11 +576,27 @@ namespace DirectX //--------------------------------------------------------------------------------- // Image I/O + class DIRECTX_TEX_API InputStream + { + public: + virtual ~InputStream() = default; + + virtual bool Read(_Out_writes_bytes_(size) void* data, _In_ size_t size) = 0; + + virtual bool Seek(_In_ size_t position) = 0; + + virtual size_t Size() = 0; + }; + // DDS operations DIRECTX_TEX_API HRESULT __cdecl LoadFromDDSMemory( _In_reads_bytes_(size) const uint8_t* pSource, _In_ size_t size, _In_ DDS_FLAGS flags, _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image) noexcept; + DIRECTX_TEX_API HRESULT __cdecl LoadFromDDSStream( + _In_ InputStream& stream, + _In_ DDS_FLAGS flags, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image) noexcept; DIRECTX_TEX_API HRESULT __cdecl LoadFromDDSFile( _In_z_ const wchar_t* szFile, _In_ DDS_FLAGS flags, @@ -592,6 +608,12 @@ namespace DirectX _Out_opt_ TexMetadata* metadata, _Out_opt_ DDSMetaData* ddPixelFormat, _Out_ ScratchImage& image) noexcept; + DIRECTX_TEX_API HRESULT __cdecl LoadFromDDSStreamEx( + _In_ InputStream& stream, + _In_ DDS_FLAGS flags, + _Out_opt_ TexMetadata* metadata, + _Out_opt_ DDSMetaData* ddPixelFormat, + _Out_ ScratchImage& image) noexcept; DIRECTX_TEX_API HRESULT __cdecl LoadFromDDSFileEx( _In_z_ const wchar_t* szFile, _In_ DDS_FLAGS flags, diff --git a/DirectXTex/DirectXTexDDS.cpp b/DirectXTex/DirectXTexDDS.cpp index 48a2a6d0..69f5ef28 100644 --- a/DirectXTex/DirectXTexDDS.cpp +++ b/DirectXTex/DirectXTexDDS.cpp @@ -2112,6 +2112,190 @@ HRESULT DirectX::LoadFromDDSMemoryEx( } +//------------------------------------------------------------------------------------- +// Load a DDS file from stream +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::LoadFromDDSStream( + InputStream& stream, + DDS_FLAGS flags, + TexMetadata* metadata, + ScratchImage& image) noexcept +{ + return LoadFromDDSStreamEx(stream, flags, metadata, nullptr, image); +} + +_Use_decl_annotations_ +HRESULT DirectX::LoadFromDDSStreamEx( + InputStream& stream, + DDS_FLAGS flags, + TexMetadata* metadata, + DDSMetaData* ddPixelFormat, + ScratchImage& image) noexcept +{ + image.Release(); + + size_t fileLen = stream.Size(); + if (fileLen == 0) + return E_FAIL; + + if (fileLen > UINT32_MAX) + return HRESULT_E_FILE_TOO_LARGE; + + const size_t len = fileLen; + + // Need at least enough data to fill the standard header and magic number to be a valid DDS + if (len < DDS_MIN_HEADER_SIZE) + { + return E_FAIL; + } + + // Read the header in (including extended header if present) + uint8_t header[DDS_DX10_HEADER_SIZE] = {}; + + const auto headerLen = std::min(len, DDS_DX10_HEADER_SIZE); + + if (!stream.Read(header, headerLen)) + return E_FAIL; + + uint32_t convFlags = 0; + TexMetadata mdata; + HRESULT hr = DecodeDDSHeader(header, headerLen, flags, mdata, ddPixelFormat, convFlags); + if (FAILED(hr)) + return hr; + + size_t offset = DDS_DX10_HEADER_SIZE; + + if (!(convFlags & CONV_FLAGS_DX10)) + { + if (!stream.Seek(DDS_MIN_HEADER_SIZE)) + return E_FAIL; + + offset = DDS_MIN_HEADER_SIZE; + } + + std::unique_ptr pal8; + if (convFlags & CONV_FLAGS_PAL8) + { + pal8.reset(new (std::nothrow) uint32_t[256]); + if (!pal8) + { + return E_OUTOFMEMORY; + } + + if (!stream.Read(pal8.get(), 256 * sizeof(uint32_t))) + return E_FAIL; + + offset += (256 * sizeof(uint32_t)); + } + + const size_t remaining = len - offset; + if (remaining == 0) + return E_FAIL; + + hr = image.Initialize(mdata); + if (FAILED(hr)) + return hr; + + if (flags & DDS_FLAGS_PERMISSIVE) + { + // For cubemaps, DDS_HEADER_DXT10.arraySize is supposed to be 'number of cubes'. + // This handles cases where the value is incorrectly written as the original 6*numCubes value. + if ((mdata.miscFlags & TEX_MISC_TEXTURECUBE) + && (convFlags & CONV_FLAGS_DX10) + && (image.GetPixelsSize() > remaining) + && ((mdata.arraySize % 6) == 0)) + { + mdata.arraySize = mdata.arraySize / 6; + hr = image.Initialize(mdata); + if (FAILED(hr)) + return hr; + + if (image.GetPixelsSize() > remaining) + { + image.Release(); + return HRESULT_E_HANDLE_EOF; + } + } + } + + if ((convFlags & CONV_FLAGS_EXPAND) || (flags & (DDS_FLAGS_LEGACY_DWORD | DDS_FLAGS_BAD_DXTN_TAILS))) + { + std::unique_ptr temp(new (std::nothrow) uint8_t[remaining]); + if (!temp) + { + image.Release(); + return E_OUTOFMEMORY; + } + + if (!stream.Read(temp.get(), remaining)) + { + image.Release(); + return E_FAIL; + } + + CP_FLAGS cflags = CP_FLAGS_NONE; + if (flags & DDS_FLAGS_LEGACY_DWORD) + { + cflags |= CP_FLAGS_LEGACY_DWORD; + } + if (flags & DDS_FLAGS_BAD_DXTN_TAILS) + { + cflags |= CP_FLAGS_BAD_DXTN_TAILS; + } + + hr = CopyImage(temp.get(), + remaining, + mdata, + cflags, + convFlags, + pal8.get(), + image); + if (FAILED(hr)) + { + image.Release(); + return hr; + } + } + else + { + if (remaining < image.GetPixelsSize()) + { + image.Release(); + return HRESULT_E_HANDLE_EOF; + } + + if (image.GetPixelsSize() > UINT32_MAX) + { + image.Release(); + return HRESULT_E_ARITHMETIC_OVERFLOW; + } + + if (!stream.Read(image.GetPixels(), image.GetPixelsSize())) + { + image.Release(); + return E_FAIL; + } + + if (convFlags & (CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA | CONV_FLAGS_L8U8V8 | CONV_FLAGS_WUV10)) + { + // Swizzle/copy image in place + hr = CopyImageInPlace(convFlags, image); + if (FAILED(hr)) + { + image.Release(); + return hr; + } + } + } + + if (metadata) + memcpy(metadata, &mdata, sizeof(TexMetadata)); + + return S_OK; +} + + //------------------------------------------------------------------------------------- // Load a DDS file from disk //-------------------------------------------------------------------------------------