From 45fb8623d271fb022a3e6211ec7a502e0cf265c6 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Sat, 9 May 2020 17:22:35 -0700 Subject: [PATCH] Direct3D 9 versions of texture loaders (#176) --- ...xtureLoader.cpp => DDSTextureLoader11.cpp} | 34 +- ...DSTextureLoader.h => DDSTextureLoader11.h} | 2 +- DDSTextureLoader/DDSTextureLoader12.cpp | 37 +- DDSTextureLoader/DDSTextureLoader9.cpp | 1278 +++++++++++++++++ DDSTextureLoader/DDSTextureLoader9.h | 78 + DirectXTex/DDS.h | 2 +- .../{ScreenGrab.cpp => ScreenGrab11.cpp} | 78 +- ScreenGrab/{ScreenGrab.h => ScreenGrab11.h} | 2 +- ScreenGrab/ScreenGrab12.cpp | 40 +- ScreenGrab/ScreenGrab9.cpp | 1010 +++++++++++++ ScreenGrab/ScreenGrab9.h | 42 + Texconv/texconv.cpp | 4 +- ...xtureLoader.cpp => WICTextureLoader11.cpp} | 63 +- ...ICTextureLoader.h => WICTextureLoader11.h} | 4 +- WICTextureLoader/WICTextureLoader12.cpp | 80 +- WICTextureLoader/WICTextureLoader12.h | 12 +- WICTextureLoader/WICTextureLoader9.cpp | 597 ++++++++ WICTextureLoader/WICTextureLoader9.h | 58 + 18 files changed, 3307 insertions(+), 114 deletions(-) rename DDSTextureLoader/{DDSTextureLoader.cpp => DDSTextureLoader11.cpp} (98%) rename DDSTextureLoader/{DDSTextureLoader.h => DDSTextureLoader11.h} (99%) create mode 100644 DDSTextureLoader/DDSTextureLoader9.cpp create mode 100644 DDSTextureLoader/DDSTextureLoader9.h rename ScreenGrab/{ScreenGrab.cpp => ScreenGrab11.cpp} (95%) rename ScreenGrab/{ScreenGrab.h => ScreenGrab11.h} (98%) create mode 100644 ScreenGrab/ScreenGrab9.cpp create mode 100644 ScreenGrab/ScreenGrab9.h rename WICTextureLoader/{WICTextureLoader.cpp => WICTextureLoader11.cpp} (95%) rename WICTextureLoader/{WICTextureLoader.h => WICTextureLoader11.h} (97%) create mode 100644 WICTextureLoader/WICTextureLoader9.cpp create mode 100644 WICTextureLoader/WICTextureLoader9.h diff --git a/DDSTextureLoader/DDSTextureLoader.cpp b/DDSTextureLoader/DDSTextureLoader11.cpp similarity index 98% rename from DDSTextureLoader/DDSTextureLoader.cpp rename to DDSTextureLoader/DDSTextureLoader11.cpp index d3c08d1..51c4bb3 100644 --- a/DDSTextureLoader/DDSTextureLoader.cpp +++ b/DDSTextureLoader/DDSTextureLoader11.cpp @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------------- -// File: DDSTextureLoader.cpp +// File: DDSTextureLoader11.cpp // // Functions for loading a DDS texture and creating a Direct3D runtime resource for it // @@ -14,7 +14,7 @@ // http://go.microsoft.com/fwlink/?LinkId=248929 //-------------------------------------------------------------------------------------- -#include "DDSTextureLoader.h" +#include "DDSTextureLoader11.h" #include #include @@ -639,12 +639,12 @@ namespace return DXGI_FORMAT_B8G8R8A8_UNORM; } - if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000)) + if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0)) { return DXGI_FORMAT_B8G8R8X8_UNORM; } - // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0x00000000) aka D3DFMT_X8B8G8R8 + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) aka 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 @@ -660,12 +660,12 @@ namespace // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 - if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000)) + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) { return DXGI_FORMAT_R16G16_UNORM; } - if (ISBITMASK(0xffffffff, 0x00000000, 0x00000000, 0x00000000)) + if (ISBITMASK(0xffffffff, 0, 0, 0)) { // Only 32-bit color channel format in D3D9 was R32F return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 @@ -681,19 +681,19 @@ namespace { return DXGI_FORMAT_B5G5R5A1_UNORM; } - if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0x0000)) + if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0)) { return DXGI_FORMAT_B5G6R5_UNORM; } - // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0x0000) aka D3DFMT_X1R5G5B5 + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) { return DXGI_FORMAT_B4G4R4A4_UNORM; } - // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0x0000) aka D3DFMT_X4R4G4B4 + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 // No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc. break; @@ -703,14 +703,14 @@ namespace { if (8 == ddpf.RGBBitCount) { - if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x00000000)) + if (ISBITMASK(0xff, 0, 0, 0)) { return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension } // No DXGI format maps to ISBITMASK(0x0f,0x00,0x00,0xf0) aka D3DFMT_A4L4 - if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00)) + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) { return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 } @@ -718,11 +718,11 @@ namespace if (16 == ddpf.RGBBitCount) { - if (ISBITMASK(0x0000ffff, 0x00000000, 0x00000000, 0x00000000)) + if (ISBITMASK(0xffff, 0, 0, 0)) { return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension } - if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00)) + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) { return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension } @@ -739,7 +739,7 @@ namespace { if (16 == ddpf.RGBBitCount) { - if (ISBITMASK(0x00ff, 0xff00, 0x0000, 0x0000)) + if (ISBITMASK(0x00ff, 0xff00, 0, 0)) { return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension } @@ -751,13 +751,15 @@ namespace { return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension } - if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000)) + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) { return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension } // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 } + + // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 } else if (ddpf.flags & DDS_FOURCC) { @@ -853,6 +855,8 @@ namespace case 116: // D3DFMT_A32B32G32R32F return DXGI_FORMAT_R32G32B32A32_FLOAT; + + // No DXGI format maps to D3DFMT_CxV8U8 } } diff --git a/DDSTextureLoader/DDSTextureLoader.h b/DDSTextureLoader/DDSTextureLoader11.h similarity index 99% rename from DDSTextureLoader/DDSTextureLoader.h rename to DDSTextureLoader/DDSTextureLoader11.h index 23eb8f1..f3f0a9e 100644 --- a/DDSTextureLoader/DDSTextureLoader.h +++ b/DDSTextureLoader/DDSTextureLoader11.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------------- -// File: DDSTextureLoader.h +// File: DDSTextureLoader11.h // // Functions for loading a DDS texture and creating a Direct3D runtime resource for it // diff --git a/DDSTextureLoader/DDSTextureLoader12.cpp b/DDSTextureLoader/DDSTextureLoader12.cpp index 70402d8..3bf3b19 100644 --- a/DDSTextureLoader/DDSTextureLoader12.cpp +++ b/DDSTextureLoader/DDSTextureLoader12.cpp @@ -27,6 +27,8 @@ #pragma clang diagnostic ignored "-Wswitch-enum" #endif +#pragma warning(disable : 4062) + #define D3DX12_NO_STATE_OBJECT_HELPERS #include "d3dx12.h" @@ -654,12 +656,12 @@ namespace return DXGI_FORMAT_B8G8R8A8_UNORM; } - if (ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0x00000000)) + if (ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0)) { return DXGI_FORMAT_B8G8R8X8_UNORM; } - // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0x00000000) aka D3DFMT_X8B8G8R8 + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0) aka 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 @@ -675,12 +677,12 @@ namespace // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 - if (ISBITMASK(0x0000ffff,0xffff0000,0x00000000,0x00000000)) + if (ISBITMASK(0x0000ffff,0xffff0000,0,0)) { return DXGI_FORMAT_R16G16_UNORM; } - if (ISBITMASK(0xffffffff,0x00000000,0x00000000,0x00000000)) + if (ISBITMASK(0xffffffff,0,0,0)) { // Only 32-bit color channel format in D3D9 was R32F return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 @@ -696,19 +698,19 @@ namespace { return DXGI_FORMAT_B5G5R5A1_UNORM; } - if (ISBITMASK(0xf800,0x07e0,0x001f,0x0000)) + if (ISBITMASK(0xf800,0x07e0,0x001f,0)) { return DXGI_FORMAT_B5G6R5_UNORM; } - // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0x0000) aka D3DFMT_X1R5G5B5 + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0) aka D3DFMT_X1R5G5B5 if (ISBITMASK(0x0f00,0x00f0,0x000f,0xf000)) { return DXGI_FORMAT_B4G4R4A4_UNORM; } - // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0x0000) aka D3DFMT_X4R4G4B4 + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0) aka D3DFMT_X4R4G4B4 // No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc. break; @@ -718,14 +720,14 @@ namespace { if (8 == ddpf.RGBBitCount) { - if (ISBITMASK(0x000000ff,0x00000000,0x00000000,0x00000000)) + if (ISBITMASK(0xff,0,0,0)) { return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension } - // No DXGI format maps to ISBITMASK(0x0f,0x00,0x00,0xf0) aka D3DFMT_A4L4 + // No DXGI format maps to ISBITMASK(0x0f,0,0,0xf0) aka D3DFMT_A4L4 - if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00)) + if (ISBITMASK(0x00ff, 0, 0, 0xff00)) { return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16 } @@ -733,11 +735,11 @@ namespace if (16 == ddpf.RGBBitCount) { - if (ISBITMASK(0x0000ffff,0x00000000,0x00000000,0x00000000)) + if (ISBITMASK(0xffff,0,0,0)) { return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension } - if (ISBITMASK(0x000000ff,0x00000000,0x00000000,0x0000ff00)) + if (ISBITMASK(0x00ff,0,0,0xff00)) { return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension } @@ -754,7 +756,7 @@ namespace { if (16 == ddpf.RGBBitCount) { - if (ISBITMASK(0x00ff, 0xff00, 0x0000, 0x0000)) + if (ISBITMASK(0x00ff, 0xff00, 0, 0)) { return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension } @@ -766,13 +768,15 @@ namespace { return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension } - if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000)) + if (ISBITMASK(0x0000ffff, 0xffff0000, 0, 0)) { return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension } // No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10 } + + // No DXGI format maps to DDPF_BUMPLUMINANCE aka D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 } else if (ddpf.flags & DDS_FOURCC) { @@ -868,6 +872,8 @@ namespace case 116: // D3DFMT_A32B32G32R32F return DXGI_FORMAT_R32G32B32A32_FLOAT; + + // No DXGI format maps to D3DFMT_CxV8U8 } } @@ -1355,7 +1361,8 @@ namespace size_t reservedMips = mipCount; if (loadFlags & DDS_LOADER_MIP_RESERVE) { - reservedMips = std::min(D3D12_REQ_MIP_LEVELS, CountMips(width, height)); + reservedMips = std::min(D3D12_REQ_MIP_LEVELS, + CountMips(width, height)); } hr = CreateTextureResource(d3dDevice, resDim, twidth, theight, tdepth, reservedMips - skipMip, arraySize, diff --git a/DDSTextureLoader/DDSTextureLoader9.cpp b/DDSTextureLoader/DDSTextureLoader9.cpp new file mode 100644 index 0000000..d440e8d --- /dev/null +++ b/DDSTextureLoader/DDSTextureLoader9.cpp @@ -0,0 +1,1278 @@ +//-------------------------------------------------------------------------------------- +// 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: + 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: + return 16; + + case D3DFMT_R3G3B2: + case D3DFMT_A8: + case D3DFMT_A8P8: + case D3DFMT_P8: + case D3DFMT_L8: + case D3DFMT_A4L4: + return 8; + + case D3DFMT_DXT1: + return 4; + + case D3DFMT_DXT2: + case D3DFMT_DXT3: + case D3DFMT_DXT4: + case D3DFMT_DXT5: + return 8; + + // 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; + + // 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'): + return 8; + + case MAKEFOURCC('Y', 'V', '1', '2'): + return 12; + +#if !defined(D3D_DISABLE_9EX) + case D3DFMT_D32_LOCKABLE: + return 32; + + case D3DFMT_S8_LOCKABLE: + return 8; + + case D3DFMT_A1: + return 1; +#endif // !D3D_DISABLE_9EX + + 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; + } + + + //-------------------------------------------------------------------------------------- + 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; +} diff --git a/DDSTextureLoader/DDSTextureLoader9.h b/DDSTextureLoader/DDSTextureLoader9.h new file mode 100644 index 0000000..d94eaa9 --- /dev/null +++ b/DDSTextureLoader/DDSTextureLoader9.h @@ -0,0 +1,78 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader9.h +// +// 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 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x900 +#endif + +#include + +#include + + +namespace DirectX +{ + // Standard version + HRESULT CreateDDSTextureFromMemory( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Outptr_ LPDIRECT3DBASETEXTURE9* texture, + bool generateMipsIfMissing = false) noexcept; + + HRESULT CreateDDSTextureFromFile( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_z_ const wchar_t* fileName, + _Outptr_ LPDIRECT3DBASETEXTURE9* texture, + bool generateMipsIfMissing = false) noexcept; + + // Type-specific versions + HRESULT CreateDDSTextureFromMemory( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Outptr_ LPDIRECT3DTEXTURE9* texture, + bool generateMipsIfMissing = false) noexcept; + + HRESULT CreateDDSTextureFromFile( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_z_ const wchar_t* fileName, + _Outptr_ LPDIRECT3DTEXTURE9* texture, + bool generateMipsIfMissing = false) noexcept; + + HRESULT CreateDDSTextureFromMemory( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Outptr_ LPDIRECT3DCUBETEXTURE9* texture) noexcept; + + HRESULT CreateDDSTextureFromFile( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_z_ const wchar_t* fileName, + _Outptr_ LPDIRECT3DCUBETEXTURE9* texture) noexcept; + + HRESULT CreateDDSTextureFromMemory( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_reads_bytes_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Outptr_ LPDIRECT3DVOLUMETEXTURE9* texture) noexcept; + + HRESULT CreateDDSTextureFromFile( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_z_ const wchar_t* fileName, + _Outptr_ LPDIRECT3DVOLUMETEXTURE9* texture) noexcept; +} diff --git a/DirectXTex/DDS.h b/DirectXTex/DDS.h index be4d7f7..b2d4e99 100644 --- a/DirectXTex/DDS.h +++ b/DirectXTex/DDS.h @@ -136,7 +136,7 @@ extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8R3G3B2 = extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R3G3B2 = { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0 }; -extern __declspec(selectany) DDS_PIXELFORMAT DDSPF_A4L4 = +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A4L4 = { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 8, 0x0f, 0, 0, 0xf0 }; extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_L8 = diff --git a/ScreenGrab/ScreenGrab.cpp b/ScreenGrab/ScreenGrab11.cpp similarity index 95% rename from ScreenGrab/ScreenGrab.cpp rename to ScreenGrab/ScreenGrab11.cpp index 2f50209..31bcf4d 100644 --- a/ScreenGrab/ScreenGrab.cpp +++ b/ScreenGrab/ScreenGrab11.cpp @@ -1,8 +1,8 @@ //-------------------------------------------------------------------------------------- -// File: ScreenGrab.cpp +// File: ScreenGrab11.cpp // // Function for capturing a 2D texture and saving it to a file (aka a 'screenshot' -// when used on a Direct3D 11 Render Target). +// when used on a Direct3D Render Target). // // Note these functions are useful as a light-weight runtime screen grabber. For // full-featured texture capture, DDS writer, and texture processing pipeline, @@ -21,7 +21,7 @@ // For 2D array textures and cubemaps, it captures only the first image in the array -#include "ScreenGrab.h" +#include "ScreenGrab11.h" #include #include @@ -33,6 +33,11 @@ #include #include +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + using Microsoft::WRL::ComPtr; //-------------------------------------------------------------------------------------- @@ -190,16 +195,16 @@ namespace { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 }; //----------------------------------------------------------------------------- - struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } }; + struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; using ScopedHandle = std::unique_ptr; - inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } + inline HANDLE safe_handle( HANDLE h ) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } class auto_delete_file { public: - auto_delete_file(HANDLE hFile) : m_handle(hFile) {} + auto_delete_file(HANDLE hFile) noexcept : m_handle(hFile) {} ~auto_delete_file() { if (m_handle) @@ -210,19 +215,22 @@ namespace } } - void clear() { m_handle = nullptr; } + auto_delete_file(const auto_delete_file&) = delete; + auto_delete_file& operator=(const auto_delete_file&) = delete; + + auto_delete_file(const auto_delete_file&&) = delete; + auto_delete_file& operator=(const auto_delete_file&&) = delete; + + void clear() noexcept { m_handle = nullptr; } private: HANDLE m_handle; - - auto_delete_file(const auto_delete_file&) = delete; - auto_delete_file& operator=(const auto_delete_file&) = delete; }; class auto_delete_file_wic { public: - auto_delete_file_wic(ComPtr& hFile, const wchar_t* szFile) : m_filename(szFile), m_handle(hFile) {} + auto_delete_file_wic(ComPtr& hFile, const wchar_t* szFile) noexcept : m_filename(szFile), m_handle(hFile) {} ~auto_delete_file_wic() { if (m_filename) @@ -232,14 +240,17 @@ namespace } } - void clear() { m_filename = nullptr; } + auto_delete_file_wic(const auto_delete_file_wic&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&) = delete; + + auto_delete_file_wic(const auto_delete_file_wic&&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&&) = delete; + + void clear() noexcept { m_filename = nullptr; } private: const wchar_t* m_filename; ComPtr& m_handle; - - auto_delete_file_wic(const auto_delete_file_wic&) = delete; - auto_delete_file_wic& operator=(const auto_delete_file_wic&) = delete; }; //-------------------------------------------------------------------------------------- @@ -793,13 +804,12 @@ HRESULT DirectX::SaveDDSTextureToFile( // Setup header const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); - uint8_t fileHeader[ MAX_HEADER_SIZE ]; + uint8_t fileHeader[MAX_HEADER_SIZE] = {}; *reinterpret_cast(&fileHeader[0]) = DDS_MAGIC; - auto header = reinterpret_cast( &fileHeader[0] + sizeof(uint32_t) ); + auto header = reinterpret_cast(&fileHeader[0] + sizeof(uint32_t)); size_t headerSize = sizeof(uint32_t) + sizeof(DDS_HEADER); - memset( header, 0, sizeof(DDS_HEADER) ); header->size = sizeof( DDS_HEADER ); header->flags = DDS_HEADER_FLAGS_TEXTURE | DDS_HEADER_FLAGS_MIPMAP; header->height = desc.Height; @@ -853,11 +863,10 @@ HRESULT DirectX::SaveDDSTextureToFile( return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); default: - memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) ); + memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT)); headerSize += sizeof(DDS_HEADER_DXT10); - extHeader = reinterpret_cast( fileHeader + sizeof(uint32_t) + sizeof(DDS_HEADER) ); - memset( extHeader, 0, sizeof(DDS_HEADER_DXT10) ); + extHeader = reinterpret_cast(fileHeader + sizeof(uint32_t) + sizeof(DDS_HEADER)); extHeader->dxgiFormat = desc.Format; extHeader->resourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D; extHeader->arraySize = 1; @@ -1165,13 +1174,21 @@ HRESULT DirectX::SaveWICTextureToFile( if ( FAILED(hr) ) return hr; + uint64_t imageSize = uint64_t(mapped.RowPitch) * uint64_t(desc.Height); + if (imageSize > UINT32_MAX) + { + pContext->Unmap(pStaging.Get(), 0); + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + if ( memcmp( &targetGuid, &pfGuid, sizeof(WICPixelFormatGUID) ) != 0 ) { // Conversion required to write ComPtr source; - hr = pWIC->CreateBitmapFromMemory( desc.Width, desc.Height, pfGuid, - mapped.RowPitch, mapped.RowPitch * desc.Height, - static_cast( mapped.pData ), source.GetAddressOf() ); + hr = pWIC->CreateBitmapFromMemory(desc.Width, desc.Height, + pfGuid, + mapped.RowPitch, static_cast(imageSize), + static_cast(mapped.pData), source.GetAddressOf()); if ( FAILED(hr) ) { pContext->Unmap( pStaging.Get(), 0 ); @@ -1190,6 +1207,7 @@ HRESULT DirectX::SaveWICTextureToFile( hr = FC->CanConvert( pfGuid, targetGuid, &canConvert ); if ( FAILED(hr) || !canConvert ) { + pContext->Unmap( pStaging.Get(), 0 ); return E_UNEXPECTED; } @@ -1202,22 +1220,18 @@ HRESULT DirectX::SaveWICTextureToFile( WICRect rect = { 0, 0, static_cast( desc.Width ), static_cast( desc.Height ) }; hr = frame->WriteSource( FC.Get(), &rect ); - if ( FAILED(hr) ) - { - pContext->Unmap( pStaging.Get(), 0 ); - return hr; - } } else { // No conversion required - hr = frame->WritePixels( desc.Height, mapped.RowPitch, mapped.RowPitch * desc.Height, static_cast( mapped.pData ) ); - if ( FAILED(hr) ) - return hr; + hr = frame->WritePixels( desc.Height, mapped.RowPitch, static_cast(imageSize), static_cast( mapped.pData ) ); } pContext->Unmap( pStaging.Get(), 0 ); + if (FAILED(hr)) + return hr; + hr = frame->Commit(); if ( FAILED(hr) ) return hr; diff --git a/ScreenGrab/ScreenGrab.h b/ScreenGrab/ScreenGrab11.h similarity index 98% rename from ScreenGrab/ScreenGrab.h rename to ScreenGrab/ScreenGrab11.h index 6f495d9..4c02c33 100644 --- a/ScreenGrab/ScreenGrab.h +++ b/ScreenGrab/ScreenGrab11.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------------- -// File: ScreenGrab.h +// File: ScreenGrab11.h // // Function for capturing a 2D texture and saving it to a file (aka a 'screenshot' // when used on a Direct3D 11 Render Target). diff --git a/ScreenGrab/ScreenGrab12.cpp b/ScreenGrab/ScreenGrab12.cpp index c51c5a6..f2f3cd7 100644 --- a/ScreenGrab/ScreenGrab12.cpp +++ b/ScreenGrab/ScreenGrab12.cpp @@ -198,16 +198,16 @@ namespace { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 }; //----------------------------------------------------------------------------- - struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } }; + struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; using ScopedHandle = std::unique_ptr; - inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } + inline HANDLE safe_handle( HANDLE h ) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } class auto_delete_file { public: - auto_delete_file(HANDLE hFile) : m_handle(hFile) {} + auto_delete_file(HANDLE hFile) noexcept : m_handle(hFile) {} ~auto_delete_file() { if (m_handle) @@ -218,19 +218,22 @@ namespace } } - void clear() { m_handle = nullptr; } + auto_delete_file(const auto_delete_file&) = delete; + auto_delete_file& operator=(const auto_delete_file&) = delete; + + auto_delete_file(const auto_delete_file&&) = delete; + auto_delete_file& operator=(const auto_delete_file&&) = delete; + + void clear() noexcept { m_handle = nullptr; } private: HANDLE m_handle; - - auto_delete_file(const auto_delete_file&) = delete; - auto_delete_file& operator=(const auto_delete_file&) = delete; }; class auto_delete_file_wic { public: - auto_delete_file_wic(ComPtr& hFile, LPCWSTR szFile) : m_filename(szFile), m_handle(hFile) {} + auto_delete_file_wic(ComPtr& hFile, LPCWSTR szFile) noexcept : m_filename(szFile), m_handle(hFile) {} ~auto_delete_file_wic() { if (m_filename) @@ -240,14 +243,17 @@ namespace } } - void clear() { m_filename = nullptr; } + auto_delete_file_wic(const auto_delete_file_wic&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&) = delete; + + auto_delete_file_wic(const auto_delete_file_wic&&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&&) = delete; + + void clear() noexcept { m_filename = nullptr; } private: LPCWSTR m_filename; ComPtr& m_handle; - - auto_delete_file_wic(const auto_delete_file_wic&) = delete; - auto_delete_file_wic& operator=(const auto_delete_file_wic&) = delete; }; //-------------------------------------------------------------------------------------- @@ -896,13 +902,12 @@ HRESULT DirectX::SaveDDSTextureToFile( // Setup header const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); - uint8_t fileHeader[ MAX_HEADER_SIZE ]; + uint8_t fileHeader[MAX_HEADER_SIZE] = {}; *reinterpret_cast(&fileHeader[0]) = DDS_MAGIC; - auto header = reinterpret_cast( &fileHeader[0] + sizeof(uint32_t) ); + auto header = reinterpret_cast(&fileHeader[0] + sizeof(uint32_t)); size_t headerSize = sizeof(uint32_t) + sizeof(DDS_HEADER); - memset( header, 0, sizeof(DDS_HEADER) ); header->size = sizeof( DDS_HEADER ); header->flags = DDS_HEADER_FLAGS_TEXTURE | DDS_HEADER_FLAGS_MIPMAP; header->height = desc.Height; @@ -956,11 +961,10 @@ HRESULT DirectX::SaveDDSTextureToFile( return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); default: - memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) ); + memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT)); headerSize += sizeof(DDS_HEADER_DXT10); - extHeader = reinterpret_cast(fileHeader + sizeof(uint32_t) + sizeof(DDS_HEADER) ); - memset( extHeader, 0, sizeof(DDS_HEADER_DXT10) ); + extHeader = reinterpret_cast(fileHeader + sizeof(uint32_t) + sizeof(DDS_HEADER)); extHeader->dxgiFormat = desc.Format; extHeader->resourceDimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; extHeader->arraySize = 1; diff --git a/ScreenGrab/ScreenGrab9.cpp b/ScreenGrab/ScreenGrab9.cpp new file mode 100644 index 0000000..c65289e --- /dev/null +++ b/ScreenGrab/ScreenGrab9.cpp @@ -0,0 +1,1010 @@ +//-------------------------------------------------------------------------------------- +// File: ScreenGrab9.cpp +// +// Function for saving 2D surface to a file (aka a 'screenshot' +// when used on a Direct3D 9's GetFrontBufferData). +// +// Note these functions are useful as a light-weight runtime screen grabber. For +// full-featured texture capture, DDS 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 "ScreenGrab9.h" + +#include + +#include + +#include + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wswitch-enum" +#endif + +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 +//-------------------------------------------------------------------------------------- +namespace +{ + #pragma pack(push,1) + + #define 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_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS + #define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE + #define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS + #define DDS_ALPHA 0x00000002 // DDPF_ALPHA + #define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV + #define DDS_BUMPLUMINANCE 0x00040000 // DDPF_BUMPLUMINANCE + + #define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT + #define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT + #define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH + #define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + + #define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE + + 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) + + const DDS_PIXELFORMAT DDSPF_DXT1 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_DXT2 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','2'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_DXT3 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_DXT4 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_DXT5 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_YUY2 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_UYVY = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('U','Y','V','Y'), 0, 0, 0, 0, 0 }; + + const DDS_PIXELFORMAT DDSPF_A8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; + + const DDS_PIXELFORMAT DDSPF_X8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0 }; + + const DDS_PIXELFORMAT DDSPF_A8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + + const DDS_PIXELFORMAT DDSPF_X8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0 }; + + const DDS_PIXELFORMAT DDSPF_G16R16 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; + + const DDS_PIXELFORMAT DDSPF_R5G6B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0xf800, 0x07e0, 0x001f, 0 }; + + const DDS_PIXELFORMAT DDSPF_A1R5G5B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x8000 }; + + const DDS_PIXELFORMAT DDSPF_X1R5G5B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x7c00, 0x03e0, 0x001f, 0 }; + + const DDS_PIXELFORMAT DDSPF_A4R4G4B4 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x0f00, 0x00f0, 0x0000f, 0xf000 }; + + const DDS_PIXELFORMAT DDSPF_X4R4G4B4 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0 }; + + const DDS_PIXELFORMAT DDSPF_R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 24, 0xff0000, 0x00ff00, 0x0000ff, 0 }; + + const DDS_PIXELFORMAT DDSPF_A8R3G3B2 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00e0, 0x001c, 0x0003, 0xff00 }; + + const DDS_PIXELFORMAT DDSPF_R3G3B2 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0 }; + + const DDS_PIXELFORMAT DDSPF_A4L4 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 8, 0x0f, 0, 0, 0xf0 }; + + const DDS_PIXELFORMAT DDSPF_L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0xff, 0x00, 0x00, 0x00 }; + + const DDS_PIXELFORMAT DDSPF_L16 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000 }; + + const DDS_PIXELFORMAT DDSPF_A8L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00 }; + + const DDS_PIXELFORMAT DDSPF_A8 = + { sizeof(DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff }; + + const DDS_PIXELFORMAT DDSPF_V8U8 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 16, 0x00ff, 0xff00, 0x0000, 0x0000 }; + + const DDS_PIXELFORMAT DDSPF_Q8W8V8U8 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + + const DDS_PIXELFORMAT DDSPF_V16U16 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; + + const DDS_PIXELFORMAT DDSPF_A2W10V10U10 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPDUDV, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }; + + const DDS_PIXELFORMAT DDSPF_L6V5U5 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPLUMINANCE, 0, 16, 0x001f, 0x03e0, 0xfc00, 0 }; + + const DDS_PIXELFORMAT DDSPF_X8L8V8U8 = + { sizeof(DDS_PIXELFORMAT), DDS_BUMPLUMINANCE, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0 }; + + // Note these 10:10:10:2 format RGB masks are reversed to support a long-standing bug in D3DX + const DDS_PIXELFORMAT DDSPF_A2R10G10B10 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 }; + const DDS_PIXELFORMAT DDSPF_A2B10G10R10 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }; + + //----------------------------------------------------------------------------- + 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; } + + class auto_delete_file + { + public: + auto_delete_file(HANDLE hFile) noexcept : m_handle(hFile) {} + ~auto_delete_file() + { + if (m_handle) + { + FILE_DISPOSITION_INFO info = {}; + info.DeleteFile = TRUE; + (void)SetFileInformationByHandle(m_handle, FileDispositionInfo, &info, sizeof(info)); + } + } + + auto_delete_file(const auto_delete_file&) = delete; + auto_delete_file& operator=(const auto_delete_file&) = delete; + + auto_delete_file(const auto_delete_file&&) = delete; + auto_delete_file& operator=(const auto_delete_file&&) = delete; + + void clear() noexcept { m_handle = nullptr; } + + private: + HANDLE m_handle; + }; + + class auto_delete_file_wic + { + public: + auto_delete_file_wic(ComPtr& hFile, const wchar_t* szFile) noexcept : m_filename(szFile), m_handle(hFile) {} + ~auto_delete_file_wic() + { + if (m_filename) + { + m_handle.Reset(); + DeleteFileW(m_filename); + } + } + + auto_delete_file_wic(const auto_delete_file_wic&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&) = delete; + + auto_delete_file_wic(const auto_delete_file_wic&&) = delete; + auto_delete_file_wic& operator=(const auto_delete_file_wic&&) = delete; + + void clear() noexcept { m_filename = nullptr; } + + private: + const wchar_t* m_filename; + ComPtr& m_handle; + }; + + + //-------------------------------------------------------------------------------------- + // 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: + 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: + return 16; + + case D3DFMT_R3G3B2: + case D3DFMT_A8: + case D3DFMT_A8P8: + case D3DFMT_P8: + case D3DFMT_L8: + case D3DFMT_A4L4: + return 8; + + case D3DFMT_DXT1: + return 4; + + case D3DFMT_DXT2: + case D3DFMT_DXT3: + case D3DFMT_DXT4: + case D3DFMT_DXT5: + return 8; + + // 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; + + // 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'): + return 8; + + case MAKEFOURCC('Y', 'V', '1', '2'): + return 12; + +#if !defined(D3D_DISABLE_9EX) + case D3DFMT_D32_LOCKABLE: + return 32; + + case D3DFMT_S8_LOCKABLE: + return 8; + + case D3DFMT_A1: + return 1; +#endif // !D3D_DISABLE_9EX + + default: + return 0; + } + } + + + //-------------------------------------------------------------------------------------- + // Determines if the format is block compressed + //-------------------------------------------------------------------------------------- + bool IsCompressed(_In_ D3DFORMAT fmt) noexcept + { + switch (fmt) + { + case D3DFMT_DXT1: + case D3DFMT_DXT2: + case D3DFMT_DXT3: + case D3DFMT_DXT4: + case D3DFMT_DXT5: + return true; + + default: + return false; + } + } + + + //-------------------------------------------------------------------------------------- + // 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; + } + + + //-------------------------------------------------------------------------------------- + bool g_WIC2 = false; + + BOOL WINAPI InitializeWICFactory(PINIT_ONCE, PVOID, PVOID* ifactory) noexcept + { +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE) + HRESULT hr = CoCreateInstance( + CLSID_WICImagingFactory2, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory2), + ifactory + ); + + if (SUCCEEDED(hr)) + { + // WIC2 is available on Windows 10, Windows 8.x, and Windows 7 SP1 with KB 2670838 installed + g_WIC2 = true; + return TRUE; + } + else + { + hr = CoCreateInstance( + CLSID_WICImagingFactory1, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory), + ifactory + ); + return SUCCEEDED(hr) ? TRUE : FALSE; + } +#else + return SUCCEEDED(CoCreateInstance( + CLSID_WICImagingFactory, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory), + ifactory)) ? TRUE : FALSE; +#endif + } + + IWICImagingFactory* _GetWIC() + { + static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; + + IWICImagingFactory* factory = nullptr; + if (!InitOnceExecuteOnce(&s_initOnce, + InitializeWICFactory, + nullptr, + reinterpret_cast(&factory))) + { + return nullptr; + } + + return factory; + } +} // anonymous namespace + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::SaveDDSTextureToFile( + LPDIRECT3DSURFACE9 pSource, + const wchar_t* fileName ) noexcept +{ + if ( !pSource|| !fileName ) + return E_INVALIDARG; + + D3DSURFACE_DESC desc = {}; + HRESULT hr = pSource->GetDesc(&desc); + if (FAILED(hr)) + return hr; + + if (desc.Type != D3DRTYPE_SURFACE && desc.Type != D3DRTYPE_TEXTURE) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + // Create file +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + ScopedHandle hFile( safe_handle( CreateFile2( fileName, GENERIC_WRITE | DELETE, 0, CREATE_ALWAYS, nullptr ) ) ); +#else + ScopedHandle hFile( safe_handle( CreateFileW( fileName, GENERIC_WRITE | DELETE, 0, nullptr, CREATE_ALWAYS, 0, nullptr ) ) ); +#endif + if ( !hFile ) + return HRESULT_FROM_WIN32( GetLastError() ); + + auto_delete_file delonfail(hFile.get()); + + // Setup header + const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER); + uint8_t fileHeader[MAX_HEADER_SIZE] = {}; + + *reinterpret_cast(&fileHeader[0]) = DDS_MAGIC; + + auto header = reinterpret_cast(&fileHeader[0] + sizeof(uint32_t)); + size_t headerSize = sizeof(uint32_t) + sizeof(DDS_HEADER); + header->size = sizeof(DDS_HEADER); + header->flags = DDS_HEADER_FLAGS_TEXTURE | DDS_HEADER_FLAGS_MIPMAP; + header->height = desc.Height; + header->width = desc.Width; + header->mipMapCount = 1; + header->caps = DDS_SURFACE_FLAGS_TEXTURE; + + switch( desc.Format ) + { + case D3DFMT_R8G8B8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_R8G8B8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A8R8G8B8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A8R8G8B8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_X8R8G8B8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_X8R8G8B8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_R5G6B5: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_R5G6B5, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_X1R5G5B5: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_X1R5G5B5, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A1R5G5B5: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A1R5G5B5, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A4R4G4B4: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_R3G3B2: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_R3G3B2, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A8R3G3B2: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A8R3G3B2, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_X4R4G4B4: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_X4R4G4B4, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A2B10G10R10: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A2B10G10R10, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A8B8G8R8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A8B8G8R8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_X8B8G8R8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_X8B8G8R8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_G16R16: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_G16R16, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A2R10G10B10: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A2R10G10B10, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_L8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_L8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A8L8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A8L8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A4L4: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A4L4, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_V8U8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_V8U8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_L6V5U5: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_L6V5U5, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_X8L8V8U8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_X8L8V8U8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_Q8W8V8U8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_Q8W8V8U8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_V16U16: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_V16U16, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_A2W10V10U10: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_A2W10V10U10, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_L16: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_L16, sizeof(DDS_PIXELFORMAT)); break; + + // FourCC formats + case D3DFMT_UYVY: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_UYVY, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_R8G8_B8G8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_R8G8_B8G8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_YUY2: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_YUY2, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_G8R8_G8B8: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_DXT1: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_DXT2: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DXT2, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_DXT3: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DXT3, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_DXT4: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DXT4, sizeof(DDS_PIXELFORMAT)); break; + case D3DFMT_DXT5: memcpy_s(&header->ddspf, sizeof(header->ddspf), &DDSPF_DXT5, sizeof(DDS_PIXELFORMAT)); break; + + // Legacy D3DX formats using D3DFMT enum value as FourCC + case D3DFMT_A16B16G16R16: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 36; break; + case D3DFMT_Q16W16V16U16: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 110; break; + case D3DFMT_R16F: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 111; break; + case D3DFMT_G16R16F: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 112; break; + case D3DFMT_A16B16G16R16F: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 113; break; + case D3DFMT_R32F: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 114; break; + case D3DFMT_G32R32F: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 115; break; + case D3DFMT_A32B32G32R32F: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 116; break; + case D3DFMT_CxV8U8: header->ddspf.size = sizeof(DDS_PIXELFORMAT); header->ddspf.flags = DDS_FOURCC; header->ddspf.fourCC = 117; break; + + default: + // No support for paletted formats D3DFMT_P8, D3DFMT_A8P8 + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + size_t rowPitch, slicePitch, rowCount; + hr = GetSurfaceInfo(desc.Width, desc.Height, desc.Format, &slicePitch, &rowPitch, &rowCount); + if (FAILED(hr)) + return hr; + + if (rowPitch > UINT32_MAX || slicePitch > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + + if (IsCompressed(desc.Format)) + { + header->flags |= DDS_HEADER_FLAGS_LINEARSIZE; + header->pitchOrLinearSize = static_cast(slicePitch); + } + else + { + header->flags |= DDS_HEADER_FLAGS_PITCH; + header->pitchOrLinearSize = static_cast(rowPitch); + } + + // Setup pixels + std::unique_ptr pixels(new (std::nothrow) uint8_t[slicePitch]); + if (!pixels) + return E_OUTOFMEMORY; + + D3DLOCKED_RECT lockedRect = {}; + hr = pSource->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY); + if (FAILED(hr)) + return hr; + + auto sptr = static_cast(lockedRect.pBits); + if (!sptr) + { + pSource->UnlockRect(); + return E_POINTER; + } + + uint8_t* dptr = pixels.get(); + + size_t msize = std::min(rowPitch, static_cast(lockedRect.Pitch)); + for( size_t h = 0; h < rowCount; ++h ) + { + memcpy_s( dptr, rowPitch, sptr, msize ); + sptr += lockedRect.Pitch; + dptr += rowPitch; + } + + pSource->UnlockRect(); + + // Write header & pixels + DWORD bytesWritten; + if ( !WriteFile( hFile.get(), fileHeader, static_cast( headerSize ), &bytesWritten, nullptr ) ) + return HRESULT_FROM_WIN32( GetLastError() ); + + if ( bytesWritten != headerSize ) + return E_FAIL; + + if ( !WriteFile( hFile.get(), pixels.get(), static_cast( slicePitch ), &bytesWritten, nullptr ) ) + return HRESULT_FROM_WIN32( GetLastError() ); + + if ( bytesWritten != slicePitch ) + return E_FAIL; + + delonfail.clear(); + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::SaveWICTextureToFile( + LPDIRECT3DSURFACE9 pSource, + REFGUID guidContainerFormat, + const wchar_t* fileName, + const GUID* targetFormat, + std::function setCustomProps) noexcept +{ + if (!pSource || !fileName) + return E_INVALIDARG; + + D3DSURFACE_DESC desc = {}; + HRESULT hr = pSource->GetDesc(&desc); + if (FAILED(hr)) + return hr; + + if (desc.Type != D3DRTYPE_SURFACE && desc.Type != D3DRTYPE_TEXTURE) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) + { + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + auto pWIC = _GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + // Determine source format's WIC equivalent + WICPixelFormatGUID pfGuid = {}; + switch ( desc.Format ) + { + case D3DFMT_R8G8B8: pfGuid = GUID_WICPixelFormat24bppBGR; break; + case D3DFMT_A8R8G8B8: pfGuid = GUID_WICPixelFormat32bppBGRA; break; + case D3DFMT_X8R8G8B8: pfGuid = GUID_WICPixelFormat32bppBGR; break; + case D3DFMT_R5G6B5: pfGuid = GUID_WICPixelFormat16bppBGR565; break; + case D3DFMT_X1R5G5B5: pfGuid = GUID_WICPixelFormat16bppBGR555; break; + case D3DFMT_A1R5G5B5: pfGuid = GUID_WICPixelFormat16bppBGRA5551; break; + case D3DFMT_A8: pfGuid = GUID_WICPixelFormat8bppAlpha; break; + case D3DFMT_A2B10G10R10: pfGuid = GUID_WICPixelFormat32bppRGBA1010102; break; + case D3DFMT_A8B8G8R8: pfGuid = GUID_WICPixelFormat32bppRGBA; break; + case D3DFMT_A16B16G16R16: pfGuid = GUID_WICPixelFormat64bppRGBA; break; + case D3DFMT_L8: pfGuid = GUID_WICPixelFormat8bppGray; break; + case D3DFMT_L16: pfGuid = GUID_WICPixelFormat16bppGray; break; + case D3DFMT_R16F: pfGuid = GUID_WICPixelFormat16bppGrayHalf; break; + case D3DFMT_A16B16G16R16F: pfGuid = GUID_WICPixelFormat64bppRGBAHalf; break; + case D3DFMT_R32F: pfGuid = GUID_WICPixelFormat32bppGrayFloat; break; + case D3DFMT_A32B32G32R32F: pfGuid = GUID_WICPixelFormat128bppRGBAFloat; break; + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE) + case D3DFMT_X8B8G8R8: + if (g_WIC2) + pfGuid = GUID_WICPixelFormat32bppRGB; + else + HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + break; +#endif + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + ComPtr stream; + hr = pWIC->CreateStream( stream.GetAddressOf() ); + if ( FAILED(hr) ) + return hr; + + hr = stream->InitializeFromFilename( fileName, GENERIC_WRITE ); + if ( FAILED(hr) ) + return hr; + + auto_delete_file_wic delonfail(stream, fileName); + + ComPtr encoder; + hr = pWIC->CreateEncoder( guidContainerFormat, nullptr, encoder.GetAddressOf() ); + if ( FAILED(hr) ) + return hr; + + hr = encoder->Initialize( stream.Get(), WICBitmapEncoderNoCache ); + if ( FAILED(hr) ) + return hr; + + ComPtr frame; + ComPtr props; + hr = encoder->CreateNewFrame( frame.GetAddressOf(), props.GetAddressOf() ); + if ( FAILED(hr) ) + return hr; + + if (targetFormat && memcmp(&guidContainerFormat, &GUID_ContainerFormatBmp, sizeof(WICPixelFormatGUID)) == 0 && g_WIC2) + { + // Opt-in to the WIC2 support for writing 32-bit Windows BMP files with an alpha channel + PROPBAG2 option = {}; + option.pstrName = const_cast(L"EnableV5Header32bppBGRA"); + + VARIANT varValue; + varValue.vt = VT_BOOL; + varValue.boolVal = VARIANT_TRUE; + (void)props->Write(1, &option, &varValue); + } + + if ( setCustomProps ) + { + setCustomProps( props.Get() ); + } + + hr = frame->Initialize( props.Get() ); + if ( FAILED(hr) ) + return hr; + + hr = frame->SetSize( desc.Width , desc.Height ); + if ( FAILED(hr) ) + return hr; + + hr = frame->SetResolution( 72, 72 ); + if ( FAILED(hr) ) + return hr; + + // Pick a target format + WICPixelFormatGUID targetGuid = {}; + if ( targetFormat ) + { + targetGuid = *targetFormat; + } + else + { + // Screenshots don't typically include the alpha channel of the render target + switch ( desc.Format ) + { +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE) + case D3DFMT_A32B32G32R32F: + case D3DFMT_A16B16G16R16F: + if (g_WIC2) + { + targetGuid = GUID_WICPixelFormat96bppRGBFloat; + } + else + { + targetGuid = GUID_WICPixelFormat24bppBGR; + } + break; +#endif + + case D3DFMT_A16B16G16R16: targetGuid = GUID_WICPixelFormat48bppBGR; break; + case D3DFMT_R5G6B5: targetGuid = GUID_WICPixelFormat16bppBGR565; break; + + case D3DFMT_A1R5G5B5: + case D3DFMT_X1R5G5B5: + targetGuid = GUID_WICPixelFormat16bppBGR555; + break; + + case D3DFMT_L16: + targetGuid = GUID_WICPixelFormat16bppGray; + break; + + case D3DFMT_R32F: + case D3DFMT_R16F: + case D3DFMT_L8: + targetGuid = GUID_WICPixelFormat8bppGray; + break; + + default: + targetGuid = GUID_WICPixelFormat24bppBGR; + break; + } + } + + hr = frame->SetPixelFormat( &targetGuid ); + if ( FAILED(hr) ) + return hr; + + if ( targetFormat && memcmp( targetFormat, &targetGuid, sizeof(WICPixelFormatGUID) ) != 0 ) + { + // Requested output pixel format is not supported by the WIC codec + return E_FAIL; + } + + // Encode WIC metadata + ComPtr metawriter; + if ( SUCCEEDED( frame->GetMetadataQueryWriter( metawriter.GetAddressOf() ) ) ) + { + PROPVARIANT value; + PropVariantInit( &value ); + + value.vt = VT_LPSTR; + value.pszVal = const_cast("DirectXTK"); + + if ( memcmp( &guidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID) ) == 0 ) + { + // Set Software name + (void)metawriter->SetMetadataByName( L"/tEXt/{str=Software}", &value ); + } + else + { + // Set Software name + (void)metawriter->SetMetadataByName( L"System.ApplicationName", &value ); + } + } + + D3DLOCKED_RECT lockedRect = {}; + hr = pSource->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY); + if (FAILED(hr)) + return hr; + + uint64_t imageSize = uint64_t(lockedRect.Pitch) * uint64_t(desc.Height); + if (imageSize > UINT32_MAX) + { + pSource->UnlockRect(); + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + + if ( memcmp( &targetGuid, &pfGuid, sizeof(WICPixelFormatGUID) ) != 0 ) + { + // Conversion required to write + ComPtr source; + hr = pWIC->CreateBitmapFromMemory(desc.Width, desc.Height, pfGuid, + static_cast(lockedRect.Pitch), static_cast(imageSize), + static_cast(lockedRect.pBits), source.GetAddressOf()); + if ( FAILED(hr) ) + { + pSource->UnlockRect(); + return hr; + } + + ComPtr FC; + hr = pWIC->CreateFormatConverter( FC.GetAddressOf() ); + if ( FAILED(hr) ) + { + pSource->UnlockRect(); + return hr; + } + + BOOL canConvert = FALSE; + hr = FC->CanConvert( pfGuid, targetGuid, &canConvert ); + if ( FAILED(hr) || !canConvert ) + { + pSource->UnlockRect(); + return E_UNEXPECTED; + } + + hr = FC->Initialize( source.Get(), targetGuid, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeMedianCut ); + if ( FAILED(hr) ) + { + pSource->UnlockRect(); + return hr; + } + + WICRect rect = { 0, 0, static_cast( desc.Width ), static_cast( desc.Height ) }; + hr = frame->WriteSource( FC.Get(), &rect ); + } + else + { + // No conversion required + hr = frame->WritePixels(desc.Height, + static_cast(lockedRect.Pitch), static_cast(imageSize), + static_cast(lockedRect.pBits)); + } + + pSource->UnlockRect(); + + if (FAILED(hr)) + return hr; + + hr = frame->Commit(); + if ( FAILED(hr) ) + return hr; + + hr = encoder->Commit(); + if ( FAILED(hr) ) + return hr; + + delonfail.clear(); + + return S_OK; +} diff --git a/ScreenGrab/ScreenGrab9.h b/ScreenGrab/ScreenGrab9.h new file mode 100644 index 0000000..93ce1c5 --- /dev/null +++ b/ScreenGrab/ScreenGrab9.h @@ -0,0 +1,42 @@ +//-------------------------------------------------------------------------------------- +// File: ScreenGrab9.h +// +// Function for saving 2D surface to a file (aka a 'screenshot' +// when used on a Direct3D 9's GetFrontBufferData). +// +// Note these functions are useful as a light-weight runtime screen grabber. For +// full-featured texture capture, DDS 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 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x900 +#endif + +#include + +#include +#include + + +namespace DirectX +{ + HRESULT __cdecl SaveDDSTextureToFile( + _In_ LPDIRECT3DSURFACE9 pSource, + _In_z_ const wchar_t* fileName) noexcept; + + HRESULT __cdecl SaveWICTextureToFile( + _In_ LPDIRECT3DSURFACE9 pSource, + _In_ REFGUID guidContainerFormat, + _In_z_ const wchar_t* fileName, + _In_opt_ const GUID* targetFormat = nullptr, + _In_opt_ std::function setCustomProps = nullptr) noexcept; +} diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index aecdc11..1a553cc 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -923,7 +923,7 @@ namespace float bestScore = FLT_MAX; for (size_t y = maxsize; y > 0; y >>= 1) { - float score = fabs((float(x) / float(y)) - origAR); + float score = fabsf((float(x) / float(y)) - origAR); if (score < bestScore) { bestScore = score; @@ -940,7 +940,7 @@ namespace float bestScore = FLT_MAX; for (size_t x = maxsize; x > 0; x >>= 1) { - float score = fabs((float(x) / float(y)) - origAR); + float score = fabsf((float(x) / float(y)) - origAR); if (score < bestScore) { bestScore = score; diff --git a/WICTextureLoader/WICTextureLoader.cpp b/WICTextureLoader/WICTextureLoader11.cpp similarity index 95% rename from WICTextureLoader/WICTextureLoader.cpp rename to WICTextureLoader/WICTextureLoader11.cpp index 92edcbb..b1797b3 100644 --- a/WICTextureLoader/WICTextureLoader.cpp +++ b/WICTextureLoader/WICTextureLoader11.cpp @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------------- -// File: WICTextureLoader.cpp +// File: WICTextureLoader11.cpp // // Function for loading a WIC image and creating a Direct3D runtime texture for it // (auto-generating mipmaps if possible) @@ -24,7 +24,7 @@ // We could load multi-frame images (TIFF/GIF) into a texture array. // For now, we just load the first frame (note: DirectXTex supports multi-frame images) -#include "WICTextureLoader.h" +#include "WICTextureLoader11.h" #include #include @@ -164,7 +164,6 @@ namespace bool g_WIC2 = false; - //-------------------------------------------------------------------------------------- BOOL WINAPI InitializeWICFactory(PINIT_ONCE, PVOID, PVOID* ifactory) noexcept { #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE) @@ -302,6 +301,48 @@ namespace } + //--------------------------------------------------------------------------------- + void FitPowerOf2(UINT origx, UINT origy, UINT& targetx, UINT& targety, size_t maxsize) + { + float origAR = float(origx) / float(origy); + + if (origx > origy) + { + size_t x; + for (x = maxsize; x > 1; x >>= 1) { if (x <= targetx) break; } + targetx = UINT(x); + + float bestScore = FLT_MAX; + for (size_t y = maxsize; y > 0; y >>= 1) + { + float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targety = UINT(y); + } + } + } + else + { + size_t y; + for (y = maxsize; y > 1; y >>= 1) { if (y <= targety) break; } + targety = UINT(y); + + float bestScore = FLT_MAX; + for (size_t x = maxsize; x > 0; x >>= 1) + { + float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targetx = UINT(x); + } + } + } + } + + //--------------------------------------------------------------------------------- HRESULT CreateTextureFromWIC(_In_ ID3D11Device* d3dDevice, _In_opt_ ID3D11DeviceContext* d3dContext, @@ -355,8 +396,13 @@ namespace assert(maxsize > 0); - UINT twidth, theight; - if (width > maxsize || height > maxsize) + UINT twidth = width; + UINT theight = height; + if (loadFlags & WIC_LOADER_FIT_POW2) + { + FitPowerOf2(width, height, twidth, theight, maxsize); + } + else if (width > maxsize || height > maxsize) { float ar = static_cast(height) / static_cast(width); if (width > height) @@ -371,10 +417,11 @@ namespace } assert(twidth <= maxsize && theight <= maxsize); } - else + + if (loadFlags & WIC_LOADER_MAKE_SQUARE) { - twidth = width; - theight = height; + twidth = std::max(twidth, theight); + theight = twidth; } // Determine format diff --git a/WICTextureLoader/WICTextureLoader.h b/WICTextureLoader/WICTextureLoader11.h similarity index 97% rename from WICTextureLoader/WICTextureLoader.h rename to WICTextureLoader/WICTextureLoader11.h index 29fb9c4..802cc7e 100644 --- a/WICTextureLoader/WICTextureLoader.h +++ b/WICTextureLoader/WICTextureLoader11.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------------------- -// File: WICTextureLoader.h +// File: WICTextureLoader11.h // // Function for loading a WIC image and creating a Direct3D runtime texture for it // (auto-generating mipmaps if possible) @@ -37,6 +37,8 @@ namespace DirectX WIC_LOADER_FORCE_SRGB = 0x1, WIC_LOADER_IGNORE_SRGB = 0x2, WIC_LOADER_FORCE_RGBA32 = 0x10, + WIC_LOADER_FIT_POW2 = 0x20, + WIC_LOADER_MAKE_SQUARE = 0x40, }; #endif diff --git a/WICTextureLoader/WICTextureLoader12.cpp b/WICTextureLoader/WICTextureLoader12.cpp index a2c99ed..e8d249a 100644 --- a/WICTextureLoader/WICTextureLoader12.cpp +++ b/WICTextureLoader/WICTextureLoader12.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------------- // File: WICTextureLoader12.cpp // -// Function for loading a WIC image and creating a Direct3D 12 runtime texture for it +// Function for loading a WIC image and creating a Direct3D runtime texture for it // (auto-generating mipmaps if possible) // // Note: Assumes application has already called CoInitializeEx @@ -166,7 +166,8 @@ namespace static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; IWICImagingFactory2* factory = nullptr; - if (!InitOnceExecuteOnce(&s_initOnce, + if (!InitOnceExecuteOnce( + &s_initOnce, InitializeWICFactory, nullptr, reinterpret_cast(&factory))) @@ -276,6 +277,47 @@ namespace return bpp; } + //--------------------------------------------------------------------------------- + void FitPowerOf2(UINT origx, UINT origy, UINT& targetx, UINT& targety, size_t maxsize) + { + float origAR = float(origx) / float(origy); + + if (origx > origy) + { + size_t x; + for (x = maxsize; x > 1; x >>= 1) { if (x <= targetx) break; } + targetx = UINT(x); + + float bestScore = FLT_MAX; + for (size_t y = maxsize; y > 0; y >>= 1) + { + float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targety = UINT(y); + } + } + } + else + { + size_t y; + for (y = maxsize; y > 1; y >>= 1) { if (y <= targety) break; } + targety = UINT(y); + + float bestScore = FLT_MAX; + for (size_t x = maxsize; x > 0; x >>= 1) + { + float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targetx = UINT(x); + } + } + } + } + //--------------------------------------------------------------------------------- HRESULT CreateTextureFromWIC(_In_ ID3D12Device* d3dDevice, _In_ IWICBitmapFrameDecode *frame, @@ -298,11 +340,16 @@ namespace if (!maxsize) { - maxsize = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; + maxsize = size_t(D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION); } - UINT twidth, theight; - if (width > maxsize || height > maxsize) + UINT twidth = width; + UINT theight = height; + if (loadFlags & WIC_LOADER_FIT_POW2) + { + FitPowerOf2(width, height, twidth, theight, maxsize); + } + else if (width > maxsize || height > maxsize) { float ar = static_cast(height) / static_cast(width); if (width > height) @@ -317,10 +364,11 @@ namespace } assert(twidth <= maxsize && theight <= maxsize); } - else + + if (loadFlags & WIC_LOADER_MAKE_SQUARE) { - twidth = width; - theight = height; + twidth = std::max(twidth, theight); + theight = twidth; } // Determine format @@ -512,7 +560,8 @@ namespace } // Count the number of mips - uint32_t mipCount = (loadFlags & (WIC_LOADER_MIP_AUTOGEN|WIC_LOADER_MIP_RESERVE)) ? CountMips(twidth, theight) : 1; + uint32_t mipCount = (loadFlags & (WIC_LOADER_MIP_AUTOGEN | WIC_LOADER_MIP_RESERVE)) + ? CountMips(twidth, theight) : 1u; // Create texture D3D12_RESOURCE_DESC desc = {}; @@ -559,7 +608,7 @@ namespace _In_ ID3D12Resource** texture) noexcept { #if !defined(NO_D3D12_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) ) - if (texture) + if (texture && *texture) { const wchar_t* pstrName = wcsrchr(fileName, '\\'); if (!pstrName) @@ -571,10 +620,7 @@ namespace pstrName++; } - if (texture && *texture) - { - (*texture)->SetName(pstrName); - } + (*texture)->SetName(pstrName); } #else UNREFERENCED_PARAMETER(fileName); @@ -722,7 +768,11 @@ HRESULT DirectX::LoadWICTextureFromFileEx( // Initialize WIC ComPtr decoder; - HRESULT hr = pWIC->CreateDecoderFromFilename(fileName, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf()); + HRESULT hr = pWIC->CreateDecoderFromFilename(fileName, + nullptr, + GENERIC_READ, + WICDecodeMetadataCacheOnDemand, + decoder.GetAddressOf()); if (FAILED(hr)) return hr; diff --git a/WICTextureLoader/WICTextureLoader12.h b/WICTextureLoader/WICTextureLoader12.h index fecc343..780ea27 100644 --- a/WICTextureLoader/WICTextureLoader12.h +++ b/WICTextureLoader/WICTextureLoader12.h @@ -32,12 +32,14 @@ namespace DirectX #define WIC_LOADER_FLAGS_DEFINED enum WIC_LOADER_FLAGS : uint32_t { - WIC_LOADER_DEFAULT = 0, - WIC_LOADER_FORCE_SRGB = 0x1, - WIC_LOADER_IGNORE_SRGB = 0x2, - WIC_LOADER_MIP_AUTOGEN = 0x4, - WIC_LOADER_MIP_RESERVE = 0x8, + WIC_LOADER_DEFAULT = 0, + WIC_LOADER_FORCE_SRGB = 0x1, + WIC_LOADER_IGNORE_SRGB = 0x2, + WIC_LOADER_MIP_AUTOGEN = 0x4, + WIC_LOADER_MIP_RESERVE = 0x8, WIC_LOADER_FORCE_RGBA32 = 0x10, + WIC_LOADER_FIT_POW2 = 0x20, + WIC_LOADER_MAKE_SQUARE = 0x40, }; #endif diff --git a/WICTextureLoader/WICTextureLoader9.cpp b/WICTextureLoader/WICTextureLoader9.cpp new file mode 100644 index 0000000..01a72d0 --- /dev/null +++ b/WICTextureLoader/WICTextureLoader9.cpp @@ -0,0 +1,597 @@ +//-------------------------------------------------------------------------------------- +// File: WICTextureLoader9.cpp +// +// Function for loading a WIC image and creating a Direct3D runtime texture for it +// +// Note: Assumes application has already called CoInitializeEx +// +// Note these functions are useful for images created as simple 2D textures. For +// more complex resources, DDSTextureLoader is an excellent light-weight runtime loader. +// 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 +//-------------------------------------------------------------------------------------- + +// We could load multi-frame images (TIFF/GIF) into a texture array. +// For now, we just load the first frame (note: DirectXTex supports multi-frame images) + +#include "WICTextureLoader9.h" + +#include + +#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; + +namespace +{ + //------------------------------------------------------------------------------------- + // WIC Pixel Format Translation Data + //------------------------------------------------------------------------------------- + struct WICTranslate + { + GUID wic; + D3DFORMAT format; + }; + + const WICTranslate g_WICFormats[] = + { + { GUID_WICPixelFormat128bppRGBAFloat, D3DFMT_A32B32G32R32F }, + + { GUID_WICPixelFormat64bppRGBAHalf, D3DFMT_A16B16G16R16F }, + { GUID_WICPixelFormat64bppRGBA, D3DFMT_A16B16G16R16 }, + + { GUID_WICPixelFormat32bppBGRA, D3DFMT_A8R8G8B8 }, + + { GUID_WICPixelFormat32bppRGBA1010102, D3DFMT_A2B10G10R10 }, + + { GUID_WICPixelFormat16bppBGRA5551, D3DFMT_A1R5G5B5 }, + { GUID_WICPixelFormat16bppBGR555, D3DFMT_X1R5G5B5 }, + { GUID_WICPixelFormat16bppBGR565, D3DFMT_R5G6B5 }, + + { GUID_WICPixelFormat32bppGrayFloat, D3DFMT_R32F }, + { GUID_WICPixelFormat16bppGrayHalf, D3DFMT_R16F }, + { GUID_WICPixelFormat16bppGray, D3DFMT_L16 }, + { GUID_WICPixelFormat8bppGray, D3DFMT_L8 }, + + { GUID_WICPixelFormat8bppAlpha, D3DFMT_A8 }, + }; + + //------------------------------------------------------------------------------------- + // WIC Pixel Format nearest conversion table + //------------------------------------------------------------------------------------- + + struct WICConvert + { + GUID source; + GUID target; + }; + + const WICConvert g_WICConvert[] = + { + // Note target GUID in this conversion table must be one of those directly supported formats (above). + + { GUID_WICPixelFormatBlackWhite, GUID_WICPixelFormat8bppGray }, // D3DFMT_L8 + + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // D3DFMT_L8 + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // D3DFMT_L8 + + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // D3DFMT_R16F + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // D3DFMT_R32F + + { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // D3DFMT_A2B10G10R10 + + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat32bppBGR, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat32bppRGBA, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + + { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + { GUID_WICPixelFormat64bppBGRA, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // D3DFMT_A32B32G32R32F + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // D3DFMT_A32B32G32R32F + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // D3DFMT_A32B32G32R32F + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // D3DFMT_A32B32G32R32F + { GUID_WICPixelFormat32bppRGBE, GUID_WICPixelFormat128bppRGBAFloat }, // D3DFMT_A32B32G32R32F + + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + + #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE) + { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppBGRA }, // D3DFMT_A8R8G8B8 + { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // D3DFMT_A16B16G16R16 + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // D3DFMT_A16B16G16R16F + { GUID_WICPixelFormat96bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // D3DFMT_A32B32G32R32F + #endif + + // We don't support n-channel formats + }; + + bool g_WIC2 = false; + + //-------------------------------------------------------------------------------------- + BOOL WINAPI InitializeWICFactory(PINIT_ONCE, PVOID, PVOID* ifactory) noexcept + { +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE) + HRESULT hr = CoCreateInstance( + CLSID_WICImagingFactory2, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory2), + ifactory + ); + + if (SUCCEEDED(hr)) + { + // WIC2 is available on Windows 10, Windows 8.x, and Windows 7 SP1 with KB 2670838 installed + g_WIC2 = true; + return TRUE; + } + else + { + hr = CoCreateInstance( + CLSID_WICImagingFactory1, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory), + ifactory + ); + return SUCCEEDED(hr) ? TRUE : FALSE; + } +#else + return SUCCEEDED(CoCreateInstance( + CLSID_WICImagingFactory, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory), + ifactory)) ? TRUE : FALSE; +#endif + } + + IWICImagingFactory* _GetWIC() noexcept + { + static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT; + + IWICImagingFactory* factory = nullptr; + if (!InitOnceExecuteOnce( + &s_initOnce, + InitializeWICFactory, + nullptr, + reinterpret_cast(&factory))) + { + return nullptr; + } + + return factory; + } + + //--------------------------------------------------------------------------------- + D3DFORMAT _WICToD3D9(const GUID& guid) noexcept + { + for (size_t i = 0; i < _countof(g_WICFormats); ++i) + { + if (memcmp(&g_WICFormats[i].wic, &guid, sizeof(GUID)) == 0) + return g_WICFormats[i].format; + } + + return D3DFMT_UNKNOWN; + } + + //--------------------------------------------------------------------------------- + void FitPowerOf2(UINT origx, UINT origy, UINT& targetx, UINT& targety, size_t maxsize) + { + float origAR = float(origx) / float(origy); + + if (origx > origy) + { + size_t x; + for (x = maxsize; x > 1; x >>= 1) { if (x <= targetx) break; } + targetx = UINT(x); + + float bestScore = FLT_MAX; + for (size_t y = maxsize; y > 0; y >>= 1) + { + float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targety = UINT(y); + } + } + } + else + { + size_t y; + for (y = maxsize; y > 1; y >>= 1) { if (y <= targety) break; } + targety = UINT(y); + + float bestScore = FLT_MAX; + for (size_t x = maxsize; x > 0; x >>= 1) + { + float score = fabsf((float(x) / float(y)) - origAR); + if (score < bestScore) + { + bestScore = score; + targetx = UINT(x); + } + } + } + } + + //--------------------------------------------------------------------------------- + HRESULT CreateTextureFromWIC( + _In_ LPDIRECT3DDEVICE9 device, + _In_ IWICBitmapFrameDecode *frame, + _In_ size_t maxsize, + _In_ unsigned int loadFlags, + _Outptr_ LPDIRECT3DTEXTURE9* texture) noexcept + { + UINT width, height; + HRESULT hr = frame->GetSize(&width, &height); + if (FAILED(hr)) + return hr; + + if (maxsize > UINT32_MAX) + return E_INVALIDARG; + + assert(width > 0 && height > 0); + + if (!maxsize) + { + maxsize = 4096u /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + } + + assert(maxsize > 0); + + UINT twidth = width; + UINT theight = height; + if (loadFlags & WIC_LOADER_FIT_POW2) + { + FitPowerOf2(width, height, twidth, theight, maxsize); + } + else if (width > maxsize || height > maxsize) + { + float ar = static_cast(height) / static_cast(width); + if (width > height) + { + twidth = static_cast(maxsize); + theight = std::max(1, static_cast(static_cast(maxsize) * ar)); + } + else + { + theight = static_cast(maxsize); + twidth = std::max(1, static_cast(static_cast(maxsize) / ar)); + } + assert(twidth <= maxsize && theight <= maxsize); + } + + if (loadFlags & WIC_LOADER_MAKE_SQUARE) + { + twidth = std::max(twidth, theight); + theight = twidth; + } + + // Determine format + WICPixelFormatGUID pixelFormat; + hr = frame->GetPixelFormat(&pixelFormat); + if (FAILED(hr)) + return hr; + + WICPixelFormatGUID convertGUID; + memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &pixelFormat, sizeof(GUID)); + + D3DFORMAT format = _WICToD3D9(pixelFormat); + if (format == D3DFMT_UNKNOWN) + { + for (size_t i = 0; i < _countof(g_WICConvert); ++i) + { + if (memcmp(&g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID)) == 0) + { + memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &g_WICConvert[i].target, sizeof(GUID)); + + format = _WICToD3D9(g_WICConvert[i].target); + assert(format != D3DFMT_UNKNOWN); + break; + } + } + + if (format == D3DFMT_UNKNOWN) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + + if (loadFlags & WIC_LOADER_FORCE_RGBA32) + { + memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &GUID_WICPixelFormat32bppBGRA, sizeof(GUID)); + format = D3DFMT_A8R8G8B8; + } + + // Create texture + ComPtr pTexture; + hr = device->CreateTexture(twidth, theight, 1u, + (loadFlags & WIC_LOADER_MIP_AUTOGEN) ? D3DUSAGE_AUTOGENMIPMAP : 0u, + format, D3DPOOL_DEFAULT, + pTexture.GetAddressOf(), nullptr); + if (FAILED(hr)) + return hr; + + // Create staging texture memory + ComPtr pStagingTexture; + hr = device->CreateTexture(twidth, theight, 1u, + 0u, format, D3DPOOL_SYSTEMMEM, + pStagingTexture.GetAddressOf(), nullptr); + if (FAILED(hr)) + return hr; + + D3DLOCKED_RECT LockedRect = {}; + hr = pStagingTexture->LockRect(0, &LockedRect, nullptr, 0); + if (FAILED(hr)) + return hr; + + uint64_t numBytes = uint64_t(LockedRect.Pitch) * uint64_t(theight); + + pStagingTexture->UnlockRect(0); + + if (numBytes > UINT32_MAX) + { + return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); + } + + // Load image data + if (memcmp(&convertGUID, &pixelFormat, sizeof(GUID)) == 0 + && twidth == width + && theight == height) + { + // No format conversion or resize needed + hr = pStagingTexture->LockRect(0, &LockedRect, nullptr, 0); + if (FAILED(hr)) + return hr; + + hr = frame->CopyPixels(nullptr, static_cast(LockedRect.Pitch), static_cast(numBytes), + static_cast(LockedRect.pBits)); + + pStagingTexture->UnlockRect(0); + + if (FAILED(hr)) + return hr; + } + else if (twidth != width || theight != height) + { + // Resize + auto pWIC = _GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + ComPtr scaler; + hr = pWIC->CreateBitmapScaler(scaler.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = scaler->Initialize(frame, twidth, theight, WICBitmapInterpolationModeFant); + if (FAILED(hr)) + return hr; + + WICPixelFormatGUID pfScaler; + hr = scaler->GetPixelFormat(&pfScaler); + if (FAILED(hr)) + return hr; + + if (memcmp(&convertGUID, &pfScaler, sizeof(GUID)) == 0) + { + // No format conversion needed + hr = pStagingTexture->LockRect(0, &LockedRect, nullptr, 0); + if (FAILED(hr)) + return hr; + + hr = scaler->CopyPixels(nullptr, static_cast(LockedRect.Pitch), static_cast(numBytes), + static_cast(LockedRect.pBits)); + + pStagingTexture->UnlockRect(0); + + if (FAILED(hr)) + return hr; + } + else + { + ComPtr FC; + hr = pWIC->CreateFormatConverter(FC.GetAddressOf()); + if (FAILED(hr)) + return hr; + + BOOL canConvert = FALSE; + hr = FC->CanConvert(pfScaler, convertGUID, &canConvert); + if (FAILED(hr) || !canConvert) + { + return E_UNEXPECTED; + } + + hr = FC->Initialize(scaler.Get(), convertGUID, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut); + if (FAILED(hr)) + return hr; + + hr = pStagingTexture->LockRect(0, &LockedRect, nullptr, 0); + if (FAILED(hr)) + return hr; + + hr = FC->CopyPixels(nullptr, static_cast(LockedRect.Pitch), static_cast(numBytes), + static_cast(LockedRect.pBits)); + + pStagingTexture->UnlockRect(0); + + if (FAILED(hr)) + return hr; + } + } + else + { + // Format conversion but no resize + auto pWIC = _GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + ComPtr FC; + hr = pWIC->CreateFormatConverter(FC.GetAddressOf()); + if (FAILED(hr)) + return hr; + + BOOL canConvert = FALSE; + hr = FC->CanConvert(pixelFormat, convertGUID, &canConvert); + if (FAILED(hr) || !canConvert) + { + return E_UNEXPECTED; + } + + hr = FC->Initialize(frame, convertGUID, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut); + if (FAILED(hr)) + return hr; + + hr = pStagingTexture->LockRect(0, &LockedRect, nullptr, 0); + if (FAILED(hr)) + return hr; + + hr = FC->CopyPixels(nullptr, static_cast(LockedRect.Pitch), static_cast(numBytes), + static_cast(LockedRect.pBits)); + + pStagingTexture->UnlockRect(0); + + if (FAILED(hr)) + return hr; + } + + hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get()); + if (FAILED(hr)) + return hr; + + *texture = pTexture.Detach(); + + return S_OK; + } +} // anonymous namespace + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateWICTextureFromMemory( + LPDIRECT3DDEVICE9 d3dDevice, + const uint8_t* wicData, + size_t wicDataSize, + LPDIRECT3DTEXTURE9* texture, + size_t maxsize, + unsigned int loadFlags) noexcept +{ + if (texture) + { + *texture = nullptr; + } + + if (!d3dDevice || !wicData || !wicDataSize || !texture) + return E_INVALIDARG; + + if (!wicDataSize) + return E_FAIL; + + if (wicDataSize > UINT32_MAX) + return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE); + + auto pWIC = _GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + // Create input stream for memory + ComPtr stream; + HRESULT hr = pWIC->CreateStream(stream.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = stream->InitializeFromMemory(const_cast(wicData), static_cast(wicDataSize)); + if (FAILED(hr)) + return hr; + + // Initialize WIC + ComPtr decoder; + hr = pWIC->CreateDecoderFromStream(stream.Get(), nullptr, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ComPtr frame; + hr = decoder->GetFrame(0, frame.GetAddressOf()); + if (FAILED(hr)) + return hr; + + return CreateTextureFromWIC(d3dDevice, frame.Get(), maxsize, loadFlags, texture); +} + +//-------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::CreateWICTextureFromFile( + LPDIRECT3DDEVICE9 d3dDevice, + const wchar_t* fileName, + LPDIRECT3DTEXTURE9* texture, + size_t maxsize, + unsigned int loadFlags) noexcept +{ + if (texture) + { + *texture = nullptr; + } + + if (!d3dDevice || !fileName || !texture) + return E_INVALIDARG; + + auto pWIC = _GetWIC(); + if (!pWIC) + return E_NOINTERFACE; + + // Initialize WIC + ComPtr decoder; + HRESULT hr = pWIC->CreateDecoderFromFilename(fileName, + nullptr, + GENERIC_READ, + WICDecodeMetadataCacheOnDemand, + decoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + ComPtr frame; + hr = decoder->GetFrame(0, frame.GetAddressOf()); + if (FAILED(hr)) + return hr; + + return CreateTextureFromWIC(d3dDevice, frame.Get(), maxsize, loadFlags, texture); +} diff --git a/WICTextureLoader/WICTextureLoader9.h b/WICTextureLoader/WICTextureLoader9.h new file mode 100644 index 0000000..328fc58 --- /dev/null +++ b/WICTextureLoader/WICTextureLoader9.h @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------------------- +// File: WICTextureLoader9.h +// +// Function for loading a WIC image and creating a Direct3D runtime texture for it +// (auto-generating mipmaps if possible) +// +// Note: Assumes application has already called CoInitializeEx +// +// Note these functions are useful for images created as simple 2D textures. For +// more complex resources, DDSTextureLoader is an excellent light-weight runtime loader. +// 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 +//-------------------------------------------------------------------------------------- + +#pragma once + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x900 +#endif +#include + +#include + +namespace DirectX +{ +#ifndef WIC_LOADER_FLAGS_DEFINED +#define WIC_LOADER_FLAGS_DEFINED + enum WIC_LOADER_FLAGS : uint32_t + { + WIC_LOADER_DEFAULT = 0, + WIC_LOADER_MIP_AUTOGEN = 0x4, + WIC_LOADER_FORCE_RGBA32 = 0x10, + WIC_LOADER_FIT_POW2 = 0x20, + WIC_LOADER_MAKE_SQUARE = 0x40, + }; +#endif + + // Standard version + HRESULT CreateWICTextureFromMemory( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_reads_bytes_(wicDataSize) const uint8_t* wicData, + _In_ size_t wicDataSize, + _Outptr_ LPDIRECT3DTEXTURE9* texture, + _In_ size_t maxsize = 0, + _In_ unsigned int loadFlags = 0) noexcept; + + HRESULT CreateWICTextureFromFile( + _In_ LPDIRECT3DDEVICE9 d3dDevice, + _In_z_ const wchar_t* fileName, + _Outptr_ LPDIRECT3DTEXTURE9* texture, + _In_ size_t maxsize = 0, + _In_ unsigned int loadFlags = 0) noexcept; +}