//-------------------------------------------------------------------------------------- // File: DDSTextureLoader9.cpp // // Functions for loading a DDS texture and creating a Direct3D runtime resource for it // // Note these functions are useful as a light-weight runtime loader for DDS files. For // a full-featured DDS file reader, writer, and texture processing pipeline see // the 'Texconv' sample and the 'DirectXTex' library. // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // http://go.microsoft.com/fwlink/?LinkId=248926 // http://go.microsoft.com/fwlink/?LinkId=248929 //-------------------------------------------------------------------------------------- #include "DDSTextureLoader9.h" #include #include #include #include #include #ifdef __clang__ #pragma clang diagnostic ignored "-Wcovered-switch-default" #pragma clang diagnostic ignored "-Wswitch-enum" #endif using namespace DirectX; using Microsoft::WRL::ComPtr; //-------------------------------------------------------------------------------------- // Macros //-------------------------------------------------------------------------------------- #ifndef MAKEFOURCC #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | \ ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) #endif /* defined(MAKEFOURCC) */ //-------------------------------------------------------------------------------------- // DDS file structure definitions // // See DDS.h in the 'Texconv' sample and the 'DirectXTex' library //-------------------------------------------------------------------------------------- #pragma pack(push,1) const uint32_t DDS_MAGIC = 0x20534444; // "DDS " struct DDS_PIXELFORMAT { uint32_t size; uint32_t flags; uint32_t fourCC; uint32_t RGBBitCount; uint32_t RBitMask; uint32_t GBitMask; uint32_t BBitMask; uint32_t ABitMask; }; #define DDS_FOURCC 0x00000004 // DDPF_FOURCC #define DDS_RGB 0x00000040 // DDPF_RGB #define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE #define DDS_ALPHA 0x00000002 // DDPF_ALPHA #define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV #define DDS_BUMPLUMINANCE 0x00040000 // DDPF_BUMPLUMINANCE #define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH #define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX #define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX #define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY #define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY #define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ #define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ #define DDS_CUBEMAP_ALLFACES ( DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ ) #define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP struct DDS_HEADER { uint32_t size; uint32_t flags; uint32_t height; uint32_t width; uint32_t pitchOrLinearSize; uint32_t depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags uint32_t mipMapCount; uint32_t reserved1[11]; DDS_PIXELFORMAT ddspf; uint32_t caps; uint32_t caps2; uint32_t caps3; uint32_t caps4; uint32_t reserved2; }; #pragma pack(pop) //-------------------------------------------------------------------------------------- namespace { struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; using ScopedHandle = std::unique_ptr; inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } //-------------------------------------------------------------------------------------- HRESULT LoadTextureDataFromMemory( _In_reads_(ddsDataSize) const uint8_t* ddsData, size_t ddsDataSize, const DDS_HEADER** header, const uint8_t** bitData, size_t* bitSize) noexcept { if (!header || !bitData || !bitSize) { return E_POINTER; } if (ddsDataSize > UINT32_MAX) { return E_FAIL; } if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) { return E_FAIL; } // DDS files always start with the same magic number ("DDS ") auto dwMagicNumber = *reinterpret_cast(ddsData); if (dwMagicNumber != DDS_MAGIC) { return E_FAIL; } auto hdr = reinterpret_cast(ddsData + sizeof(uint32_t)); // Verify header to validate DDS file if (hdr->size != sizeof(DDS_HEADER) || hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) { return E_FAIL; } // Check for DX10 extension if ((hdr->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) { // We don't support the new DX10 header for Direct3D 9 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } // setup the pointers in the process request *header = hdr; auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER); *bitData = ddsData + offset; *bitSize = ddsDataSize - offset; return S_OK; } //-------------------------------------------------------------------------------------- HRESULT LoadTextureDataFromFile( _In_z_ const wchar_t* fileName, std::unique_ptr& ddsData, const DDS_HEADER** header, const uint8_t** bitData, size_t* bitSize) noexcept { if (!header || !bitData || !bitSize) { return E_POINTER; } // open the file #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) ScopedHandle hFile(safe_handle(CreateFile2(fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr))); #else ScopedHandle hFile(safe_handle(CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr))); #endif if (!hFile) { return HRESULT_FROM_WIN32(GetLastError()); } // Get the file size FILE_STANDARD_INFO fileInfo; if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) { return HRESULT_FROM_WIN32(GetLastError()); } // File is too big for 32-bit allocation, so reject read if (fileInfo.EndOfFile.HighPart > 0) { return E_FAIL; } // Need at least enough data to fill the header and magic number to be a valid DDS if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER))) { return E_FAIL; } // create enough space for the file data ddsData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); if (!ddsData) { return E_OUTOFMEMORY; } // read the data in DWORD BytesRead = 0; if (!ReadFile(hFile.get(), ddsData.get(), fileInfo.EndOfFile.LowPart, &BytesRead, nullptr )) { return HRESULT_FROM_WIN32(GetLastError()); } if (BytesRead < fileInfo.EndOfFile.LowPart) { return E_FAIL; } // DDS files always start with the same magic number ("DDS ") auto dwMagicNumber = *reinterpret_cast(ddsData.get()); if (dwMagicNumber != DDS_MAGIC) { return E_FAIL; } auto hdr = reinterpret_cast(ddsData.get() + sizeof(uint32_t)); // Verify header to validate DDS file if (hdr->size != sizeof(DDS_HEADER) || hdr->ddspf.size != sizeof(DDS_PIXELFORMAT)) { return E_FAIL; } // Check for DX10 extension if ((hdr->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == hdr->ddspf.fourCC)) { // We don't support the new DX10 header for Direct3D 9 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } // setup the pointers in the process request *header = hdr; auto offset = sizeof(uint32_t) + sizeof(DDS_HEADER); *bitData = ddsData.get() + offset; *bitSize = fileInfo.EndOfFile.LowPart - offset; return S_OK; } //-------------------------------------------------------------------------------------- // Return the BPP for a particular format //-------------------------------------------------------------------------------------- size_t BitsPerPixel(_In_ D3DFORMAT fmt) noexcept { switch (static_cast(fmt)) { case D3DFMT_A32B32G32R32F: return 128; case D3DFMT_A16B16G16R16: case D3DFMT_Q16W16V16U16: case D3DFMT_A16B16G16R16F: case D3DFMT_G32R32F: return 64; case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: case D3DFMT_A2B10G10R10: case D3DFMT_A8B8G8R8: case D3DFMT_X8B8G8R8: case D3DFMT_G16R16: case D3DFMT_A2R10G10B10: case D3DFMT_Q8W8V8U8: case D3DFMT_V16U16: case D3DFMT_X8L8V8U8: case D3DFMT_A2W10V10U10: case D3DFMT_D32: case D3DFMT_D24S8: case D3DFMT_D24X8: case D3DFMT_D24X4S4: case D3DFMT_D32F_LOCKABLE: case D3DFMT_D24FS8: case D3DFMT_INDEX32: case D3DFMT_G16R16F: case D3DFMT_R32F: #if !defined(D3D_DISABLE_9EX) case D3DFMT_D32_LOCKABLE: #endif return 32; case D3DFMT_R8G8B8: return 24; case D3DFMT_A4R4G4B4: case D3DFMT_X4R4G4B4: case D3DFMT_R5G6B5: case D3DFMT_L16: case D3DFMT_A8L8: case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5: case D3DFMT_A8R3G3B2: case D3DFMT_V8U8: case D3DFMT_CxV8U8: case D3DFMT_L6V5U5: case D3DFMT_G8R8_G8B8: case D3DFMT_R8G8_B8G8: case D3DFMT_D16_LOCKABLE: case D3DFMT_D15S1: case D3DFMT_D16: case D3DFMT_INDEX16: case D3DFMT_R16F: case D3DFMT_YUY2: // From DX docs, reference/d3d/enums/d3dformat.asp // (note how it says that D3DFMT_R8G8_B8G8 is "A 16-bit packed RGB format analogous to UYVY (U0Y0, V0Y1, U2Y2, and so on)") case D3DFMT_UYVY: return 16; case D3DFMT_R3G3B2: case D3DFMT_A8: case D3DFMT_A8P8: case D3DFMT_P8: case D3DFMT_L8: case D3DFMT_A4L4: case D3DFMT_DXT2: case D3DFMT_DXT3: case D3DFMT_DXT4: case D3DFMT_DXT5: // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/directxvideoaccelerationdxvavideosubtypes.asp case MAKEFOURCC('A', 'I', '4', '4'): case MAKEFOURCC('I', 'A', '4', '4'): #if !defined(D3D_DISABLE_9EX) case D3DFMT_S8_LOCKABLE: #endif return 8; case D3DFMT_DXT1: return 4; case MAKEFOURCC('Y', 'V', '1', '2'): return 12; #if !defined(D3D_DISABLE_9EX) case D3DFMT_A1: return 1; #endif default: return 0; } } //-------------------------------------------------------------------------------------- // Get surface information for a particular format //-------------------------------------------------------------------------------------- HRESULT GetSurfaceInfo( _In_ size_t width, _In_ size_t height, _In_ D3DFORMAT fmt, size_t* outNumBytes, _Out_opt_ size_t* outRowBytes, _Out_opt_ size_t* outNumRows) noexcept { uint64_t numBytes = 0; uint64_t rowBytes = 0; uint64_t numRows = 0; bool bc = false; bool packed = false; size_t bpe = 0; switch (static_cast(fmt)) { case D3DFMT_DXT1: bc = true; bpe = 8; break; case D3DFMT_DXT2: case D3DFMT_DXT3: case D3DFMT_DXT4: case D3DFMT_DXT5: bc = true; bpe = 16; break; case D3DFMT_R8G8_B8G8: case D3DFMT_G8R8_G8B8: case D3DFMT_UYVY: case D3DFMT_YUY2: packed = true; bpe = 4; break; default: break; } if (bc) { uint64_t numBlocksWide = 0; if (width > 0) { numBlocksWide = std::max(1u, (uint64_t(width) + 3u) / 4u); } uint64_t numBlocksHigh = 0; if (height > 0) { numBlocksHigh = std::max(1u, (uint64_t(height) + 3u) / 4u); } rowBytes = numBlocksWide * bpe; numRows = numBlocksHigh; numBytes = rowBytes * numBlocksHigh; } else if (packed) { rowBytes = ((uint64_t(width) + 1u) >> 1) * bpe; numRows = uint64_t(height); numBytes = rowBytes * height; } else { size_t bpp = BitsPerPixel(fmt); if (!bpp) return E_INVALIDARG; rowBytes = (uint64_t(width) * bpp + 7u) / 8u; // round up to nearest byte numRows = uint64_t(height); numBytes = rowBytes * height; } #if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64) static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!"); if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX) return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); #else static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!"); #endif if (outNumBytes) { *outNumBytes = static_cast(numBytes); } if (outRowBytes) { *outRowBytes = static_cast(rowBytes); } if (outNumRows) { *outNumRows = static_cast(numRows); } return S_OK; } //-------------------------------------------------------------------------------------- #define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) D3DFORMAT GetD3D9Format(const DDS_PIXELFORMAT& ddpf) noexcept { if (ddpf.flags & DDS_RGB) { switch (ddpf.RGBBitCount) { case 32: if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)) { return D3DFMT_A8R8G8B8; } if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0)) { return D3DFMT_X8R8G8B8; } if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) { return D3DFMT_A8B8G8R8; } if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0)) { return D3DFMT_X8B8G8R8; } // Note that many common DDS reader/writers (including D3DX) swap the // the RED/BLUE masks for 10:10:10:2 formats. We assume // below that the 'backwards' header mask is being used since it is most // likely written by D3DX. // For 'correct' writers this should be 0x3ff00000,0x000ffc00,0x000003ff for BGR data if (ISBITMASK(0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000)) { return D3DFMT_A2R10G10B10; } // For 'correct' writers this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) { return D3DFMT_A2B10G10R10; } if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000)) { return D3DFMT_G16R16; } if (ISBITMASK(0xffffffff, 0x00000000, 0x00000000, 0x00000000)) { return D3DFMT_R32F; // D3DX writes this out as a FourCC of 114 } break; case 24: if (ISBITMASK(0xff0000, 0x00ff00, 0x0000ff, 0)) { return D3DFMT_R8G8B8; } break; case 16: if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0x0000)) { return D3DFMT_R5G6B5; } if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000)) { return D3DFMT_A1R5G5B5; } if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0)) { return D3DFMT_X1R5G5B5; } if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) { return D3DFMT_A4R4G4B4; } if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0)) { return D3DFMT_X4R4G4B4; } if (ISBITMASK(0x00e0, 0x001c, 0x0003, 0xff00)) { return D3DFMT_A8R3G3B2; } break; case 8: if (ISBITMASK(0xe0, 0x1c, 0x03, 0)) { return D3DFMT_R3G3B2; } // Paletted texture formats are typically not supported on modern video cards aka D3DFMT_P8, D3DFMT_A8P8 break; } } else if (ddpf.flags & DDS_LUMINANCE) { if (8 == ddpf.RGBBitCount) { if (ISBITMASK(0x0f, 0, 0, 0xf0)) { return D3DFMT_A4L4; } if (ISBITMASK(0xff, 0, 0, 0)) { return D3DFMT_L8; } } if (16 == ddpf.RGBBitCount) { if (ISBITMASK(0xffff, 0, 0, 0)) { return D3DFMT_L16; } if (ISBITMASK(0x00ff, 0, 0, 0xff00)) { return D3DFMT_A8L8; } } } else if (ddpf.flags & DDS_ALPHA) { if (8 == ddpf.RGBBitCount) { return D3DFMT_A8; } } else if (ddpf.flags & DDS_BUMPDUDV) { if (16 == ddpf.RGBBitCount) { if (ISBITMASK(0x00ff, 0xff00, 0, 0)) { return D3DFMT_V8U8; } } if (32 == ddpf.RGBBitCount) { if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) { return D3DFMT_Q8W8V8U8; } if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000)) { return D3DFMT_V16U16; } if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) { return D3DFMT_A2W10V10U10; } } } else if (ddpf.flags & DDS_BUMPLUMINANCE) { if (16 == ddpf.RGBBitCount) { if (ISBITMASK(0x001f, 0x03e0, 0xfc00, 0)) { return D3DFMT_L6V5U5; } } if (32 == ddpf.RGBBitCount) { if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0)) { return D3DFMT_X8L8V8U8; } } } else if (ddpf.flags & DDS_FOURCC) { if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf.fourCC) { return D3DFMT_DXT1; } if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf.fourCC) { return D3DFMT_DXT2; } if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf.fourCC) { return D3DFMT_DXT3; } if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf.fourCC) { return D3DFMT_DXT4; } if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf.fourCC) { return D3DFMT_DXT5; } if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf.fourCC) { return D3DFMT_R8G8_B8G8; } if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf.fourCC) { return D3DFMT_G8R8_G8B8; } if (MAKEFOURCC('U', 'Y', 'V', 'Y') == ddpf.fourCC) { return D3DFMT_UYVY; } if (MAKEFOURCC('Y', 'U', 'Y', '2') == ddpf.fourCC) { return D3DFMT_YUY2; } // Check for D3DFORMAT enums being set here switch (ddpf.fourCC) { case D3DFMT_A16B16G16R16: case D3DFMT_Q16W16V16U16: case D3DFMT_R16F: case D3DFMT_G16R16F: case D3DFMT_A16B16G16R16F: case D3DFMT_R32F: case D3DFMT_G32R32F: case D3DFMT_A32B32G32R32F: case D3DFMT_CxV8U8: return static_cast(ddpf.fourCC); } } return D3DFMT_UNKNOWN; } #undef ISBITMASK //-------------------------------------------------------------------------------------- HRESULT CreateTextureFromDDS( _In_ LPDIRECT3DDEVICE9 device, _In_ const DDS_HEADER* header, _In_reads_bytes_(bitSize) const uint8_t* bitData, _In_ size_t bitSize, _Outptr_ LPDIRECT3DBASETEXTURE9* texture, bool generateMipsIfMissing) noexcept { HRESULT hr = S_OK; UINT iWidth = header->width; UINT iHeight = header->height; UINT iMipCount = header->mipMapCount; if (0 == iMipCount) { iMipCount = 1; } // Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 10 hardware requirements) if (iMipCount > 14u /*D3D10_REQ_MIP_LEVELS*/) { return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } // We could support a subset of 'DX10' extended header DDS files, but we'll assume here we are only // supporting legacy DDS files for a Direct3D9 device D3DFORMAT fmt = GetD3D9Format(header->ddspf); if (fmt == D3DFMT_UNKNOWN || BitsPerPixel(fmt) == 0) { return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } if (header->flags & DDS_HEADER_FLAGS_VOLUME) { UINT iDepth = header->depth; if ((iWidth > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/) || (iHeight > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/) || (iDepth > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/)) { return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } // Create the volume texture (let the runtime do the validation) ComPtr pTexture; hr = device->CreateVolumeTexture(iWidth, iHeight, iDepth, iMipCount, 0, fmt, D3DPOOL_DEFAULT, pTexture.GetAddressOf(), nullptr); if (FAILED(hr)) return hr; ComPtr pStagingTexture; hr = device->CreateVolumeTexture(iWidth, iHeight, iDepth, iMipCount, 0, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr); if (FAILED(hr)) return hr; // Lock, fill, unlock size_t NumBytes = 0; size_t RowBytes = 0; size_t NumRows = 0; const uint8_t* pSrcBits = bitData; const uint8_t* pEndBits = bitData + bitSize; D3DLOCKED_BOX LockedBox = {}; for (UINT i = 0; i < iMipCount; ++i) { GetSurfaceInfo(iWidth, iHeight, fmt, &NumBytes, &RowBytes, &NumRows); if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); if ((pSrcBits + (NumBytes * iDepth)) > pEndBits) { return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); } if (SUCCEEDED(pStagingTexture->LockBox(i, &LockedBox, nullptr, 0))) { auto pDestBits = static_cast(LockedBox.pBits); for (UINT j = 0; j < iDepth; ++j) { uint8_t* dptr = pDestBits; const uint8_t* sptr = pSrcBits; // Copy stride line by line for (size_t h = 0; h < NumRows; h++) { memcpy_s(dptr, static_cast(LockedBox.RowPitch), sptr, RowBytes); dptr += LockedBox.RowPitch; sptr += RowBytes; } pDestBits += LockedBox.SlicePitch; pSrcBits += NumBytes; } pStagingTexture->UnlockBox(i); } iWidth = iWidth >> 1; iHeight = iHeight >> 1; iDepth = iDepth >> 1; if (iWidth == 0) iWidth = 1; if (iHeight == 0) iHeight = 1; if (iDepth == 0) iDepth = 1; } hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get()); if (FAILED(hr)) return hr; *texture = pTexture.Detach(); } else if (header->caps2 & DDS_CUBEMAP) { if ((iWidth > 8192u /*D3D10_REQ_TEXTURECUBE_DIMENSION*/) || (iHeight > 8192u /*D3D10_REQ_TEXTURECUBE_DIMENSION*/)) { return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } // We require at least one face to be defined, and the faces must be square if ((header->caps2 & DDS_CUBEMAP_ALLFACES) == 0 || iHeight != iWidth) { return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } // Create the cubemap (let the runtime do the validation) ComPtr pTexture; hr = device->CreateCubeTexture(iWidth, iMipCount, 0, fmt, D3DPOOL_DEFAULT, pTexture.GetAddressOf(), nullptr); if (FAILED(hr)) return hr; ComPtr pStagingTexture; hr = device->CreateCubeTexture(iWidth, iMipCount, 0, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr); if (FAILED(hr)) return hr; // Lock, fill, unlock size_t NumBytes = 0; size_t RowBytes = 0; size_t NumRows = 0; const uint8_t* pSrcBits = bitData; const uint8_t* pEndBits = bitData + bitSize; D3DLOCKED_RECT LockedRect = {}; UINT mask = DDS_CUBEMAP_POSITIVEX & ~DDS_CUBEMAP; for (UINT f = 0; f < 6; ++f, mask <<= 1) { if (!(header->caps2 & mask)) continue; UINT w = iWidth; UINT h = iHeight; for (UINT i = 0; i < iMipCount; ++i) { GetSurfaceInfo(w, h, fmt, &NumBytes, &RowBytes, &NumRows); if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); if ((pSrcBits + NumBytes) > pEndBits) { return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); } if (SUCCEEDED(pStagingTexture->LockRect(static_cast(f), i, &LockedRect, nullptr, 0))) { auto pDestBits = static_cast(LockedRect.pBits); // Copy stride line by line for (size_t r = 0; r < NumRows; r++) { memcpy_s(pDestBits, static_cast(LockedRect.Pitch), pSrcBits, RowBytes); pDestBits += LockedRect.Pitch; pSrcBits += RowBytes; } pStagingTexture->UnlockRect(static_cast(f), i); } w = w >> 1; h = h >> 1; if (w == 0) w = 1; if (h == 0) h = 1; } } hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get()); if (FAILED(hr)) return hr; *texture = pTexture.Detach(); } else { if ((iWidth > 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/) || (iHeight > 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/)) { return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); } // Create the texture (let the runtime do the validation) ComPtr pTexture; hr = device->CreateTexture(iWidth, iHeight, iMipCount, generateMipsIfMissing ? D3DUSAGE_AUTOGENMIPMAP : 0u, fmt, D3DPOOL_DEFAULT, pTexture.GetAddressOf(), nullptr); if (FAILED(hr)) return hr; ComPtr pStagingTexture; hr = device->CreateTexture(iWidth, iHeight, iMipCount, 0u, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr); if (FAILED(hr)) return hr; // Lock, fill, unlock size_t NumBytes = 0; size_t RowBytes = 0; size_t NumRows = 0; const uint8_t* pSrcBits = bitData; const uint8_t* pEndBits = bitData + bitSize; D3DLOCKED_RECT LockedRect = {}; for (UINT i = 0; i < iMipCount; ++i) { GetSurfaceInfo(iWidth, iHeight, fmt, &NumBytes, &RowBytes, &NumRows); if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX) return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); if ((pSrcBits + NumBytes) > pEndBits) { return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF); } if (SUCCEEDED(pStagingTexture->LockRect(i, &LockedRect, nullptr, 0))) { auto pDestBits = static_cast(LockedRect.pBits); // Copy stride line by line for (UINT h = 0; h < NumRows; h++) { memcpy_s(pDestBits, static_cast(LockedRect.Pitch), pSrcBits, RowBytes); pDestBits += LockedRect.Pitch; pSrcBits += RowBytes; } pStagingTexture->UnlockRect(i); } iWidth = iWidth >> 1; iHeight = iHeight >> 1; if (iWidth == 0) iWidth = 1; if (iHeight == 0) iHeight = 1; } hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get()); if (FAILED(hr)) return hr; *texture = pTexture.Detach(); } return hr; } } // anonymous namespace //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory( LPDIRECT3DDEVICE9 d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, LPDIRECT3DBASETEXTURE9* texture, bool generateMipsIfMissing) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !ddsData || !texture) { return E_INVALIDARG; } // Validate DDS file in memory const DDS_HEADER* header = nullptr; const uint8_t* bitData = nullptr; size_t bitSize = 0; HRESULT hr = LoadTextureDataFromMemory(ddsData, ddsDataSize, &header, &bitData, &bitSize ); if (FAILED(hr)) return hr; return CreateTextureFromDDS( d3dDevice, header, bitData, bitSize, texture, generateMipsIfMissing); } // Type-specific versions _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory( LPDIRECT3DDEVICE9 d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, LPDIRECT3DTEXTURE9* texture, bool generateMipsIfMissing) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !ddsData || !ddsDataSize || !texture) return E_INVALIDARG; ComPtr tex; HRESULT hr = CreateDDSTextureFromMemory(d3dDevice, ddsData, ddsDataSize, tex.GetAddressOf(), generateMipsIfMissing); if (SUCCEEDED(hr)) { hr = E_FAIL; if (tex->GetType() == D3DRTYPE_TEXTURE) { *texture = static_cast(tex.Detach()); return S_OK; } } return hr; } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory( LPDIRECT3DDEVICE9 d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, LPDIRECT3DCUBETEXTURE9* texture) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !ddsData || !ddsDataSize || !texture) return E_INVALIDARG; ComPtr tex; HRESULT hr = CreateDDSTextureFromMemory(d3dDevice, ddsData, ddsDataSize, tex.GetAddressOf(), false); if (SUCCEEDED(hr)) { hr = E_FAIL; if (tex->GetType() == D3DRTYPE_CUBETEXTURE) { *texture = static_cast(tex.Detach()); return S_OK; } } return hr; } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromMemory( LPDIRECT3DDEVICE9 d3dDevice, const uint8_t* ddsData, size_t ddsDataSize, LPDIRECT3DVOLUMETEXTURE9* texture) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !ddsData || !ddsDataSize || !texture) return E_INVALIDARG; ComPtr tex; HRESULT hr = CreateDDSTextureFromMemory(d3dDevice, ddsData, ddsDataSize, tex.GetAddressOf(), false); if (SUCCEEDED(hr)) { hr = E_FAIL; if (tex->GetType() == D3DRTYPE_VOLUMETEXTURE) { *texture = static_cast(tex.Detach()); return S_OK; } } return hr; } //-------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile( LPDIRECT3DDEVICE9 d3dDevice, const wchar_t* fileName, LPDIRECT3DBASETEXTURE9* texture, bool generateMipsIfMissing) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !fileName || !texture) return E_INVALIDARG; const DDS_HEADER* header = nullptr; const uint8_t* bitData = nullptr; size_t bitSize = 0; std::unique_ptr ddsData; HRESULT hr = LoadTextureDataFromFile(fileName, ddsData, &header, &bitData, &bitSize ); if (FAILED(hr)) { return hr; } return CreateTextureFromDDS( d3dDevice, header, bitData, bitSize, texture, generateMipsIfMissing); } // Type-specific versions _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile( LPDIRECT3DDEVICE9 d3dDevice, const wchar_t* fileName, LPDIRECT3DTEXTURE9* texture, bool generateMipsIfMissing) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !fileName || !texture) return E_INVALIDARG; ComPtr tex; HRESULT hr = CreateDDSTextureFromFile(d3dDevice, fileName, tex.GetAddressOf(), generateMipsIfMissing); if (SUCCEEDED(hr)) { hr = E_FAIL; if (tex->GetType() == D3DRTYPE_TEXTURE) { *texture = static_cast(tex.Detach()); return S_OK; } } return hr; } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile( LPDIRECT3DDEVICE9 d3dDevice, const wchar_t* fileName, LPDIRECT3DCUBETEXTURE9* texture) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !fileName || !texture) return E_INVALIDARG; ComPtr tex; HRESULT hr = CreateDDSTextureFromFile(d3dDevice, fileName, tex.GetAddressOf(), false); if (SUCCEEDED(hr)) { hr = E_FAIL; if (tex->GetType() == D3DRTYPE_CUBETEXTURE) { *texture = static_cast(tex.Detach()); return S_OK; } } return hr; } _Use_decl_annotations_ HRESULT DirectX::CreateDDSTextureFromFile( LPDIRECT3DDEVICE9 d3dDevice, const wchar_t* szFileName, LPDIRECT3DVOLUMETEXTURE9* texture) noexcept { if (texture) { *texture = nullptr; } if (!d3dDevice || !szFileName || !texture) return E_INVALIDARG; ComPtr tex; HRESULT hr = CreateDDSTextureFromFile(d3dDevice, szFileName, tex.GetAddressOf(), false); if (SUCCEEDED(hr)) { hr = E_FAIL; if (tex->GetType() == D3DRTYPE_VOLUMETEXTURE) { *texture = static_cast(tex.Detach()); return S_OK; } } return hr; }