From a6c5c5939a9d3a3ca3d88a1b59a7c319d37fcf78 Mon Sep 17 00:00:00 2001 From: walbourn_cp Date: Tue, 1 May 2012 13:00:24 -0700 Subject: [PATCH] April 20, 2012 source --- DDSTextureLoader/DDSTextureLoader.cpp | 1413 +++++++++ DDSTextureLoader/DDSTextureLoader.h | 45 + DDSView/DDSView.rc | 75 + DDSView/DDSView_11_Desktop.sln | 45 + DDSView/DDSView_11_Desktop.vcxproj | 390 +++ DDSView/DDSView_11_Desktop.vcxproj.filters | 20 + DDSView/DDSView_2010.sln | 45 + DDSView/DDSView_2010.vcxproj | 383 +++ DDSView/DDSView_2010.vcxproj.filters | 20 + DDSView/ddsview.cpp | 736 +++++ DDSView/ddsview.fx | 85 + DDSView/directx.ico | Bin 0 -> 25214 bytes DDSView/hlsl.cmd | 8 + DDSView/shaders/ps1D.h | 179 ++ DDSView/shaders/ps1Darray.h | 192 ++ DDSView/shaders/ps2D.h | 144 + DDSView/shaders/ps2Darray.h | 192 ++ DDSView/shaders/ps3D.h | 198 ++ DDSView/shaders/psCube.h | 194 ++ DDSView/shaders/vs.h | 131 + DirectXTex/BC.cpp | 1131 +++++++ DirectXTex/BC.h | 897 ++++++ DirectXTex/BC4BC5.cpp | 534 ++++ DirectXTex/BC6HBC7.cpp | 2819 +++++++++++++++++ DirectXTex/DDS.h | 214 ++ DirectXTex/DirectXTex.h | 458 +++ DirectXTex/DirectXTex.inl | 223 ++ DirectXTex/DirectXTexCompress.cpp | 697 ++++ DirectXTex/DirectXTexConvert.cpp | 2393 ++++++++++++++ DirectXTex/DirectXTexD3D11.cpp | 800 +++++ DirectXTex/DirectXTexDDS.cpp | 1683 ++++++++++ DirectXTex/DirectXTexFlipRotate.cpp | 327 ++ DirectXTex/DirectXTexImage.cpp | 638 ++++ DirectXTex/DirectXTexMipmaps.cpp | 1157 +++++++ DirectXTex/DirectXTexMisc.cpp | 265 ++ DirectXTex/DirectXTexNormalMaps.cpp | 377 +++ DirectXTex/DirectXTexP.h | 209 ++ DirectXTex/DirectXTexResize.cpp | 358 +++ DirectXTex/DirectXTexTGA.cpp | 1386 ++++++++ DirectXTex/DirectXTexUtil.cpp | 525 +++ DirectXTex/DirectXTexWIC.cpp | 941 ++++++ DirectXTex/DirectXTex_11_Desktop.sln | 31 + DirectXTex/DirectXTex_11_Desktop.vcxproj | 428 +++ .../DirectXTex_11_Desktop.vcxproj.filters | 37 + DirectXTex/DirectXTex_2010.sln | 31 + DirectXTex/DirectXTex_2010.vcxproj | 421 +++ DirectXTex/DirectXTex_2010.vcxproj.filters | 37 + DirectXTex/scoped.h | 70 + Texconv/Texconv.rc | 76 + Texconv/Texconv_11_Desktop.sln | 45 + Texconv/Texconv_11_Desktop.vcxproj | 389 +++ Texconv/Texconv_11_Desktop.vcxproj.filters | 17 + Texconv/directx.ico | Bin 0 -> 25214 bytes Texconv/texconv.cpp | 1001 ++++++ Texconv/texconv_2010.sln | 45 + Texconv/texconv_2010.vcxproj | 382 +++ Texconv/texconv_2010.vcxproj.filters | 17 + WICTextureLoader/WICTextureLoader.cpp | 691 ++++ WICTextureLoader/WICTextureLoader.h | 54 + 59 files changed, 26299 insertions(+) create mode 100644 DDSTextureLoader/DDSTextureLoader.cpp create mode 100644 DDSTextureLoader/DDSTextureLoader.h create mode 100644 DDSView/DDSView.rc create mode 100644 DDSView/DDSView_11_Desktop.sln create mode 100644 DDSView/DDSView_11_Desktop.vcxproj create mode 100644 DDSView/DDSView_11_Desktop.vcxproj.filters create mode 100644 DDSView/DDSView_2010.sln create mode 100644 DDSView/DDSView_2010.vcxproj create mode 100644 DDSView/DDSView_2010.vcxproj.filters create mode 100644 DDSView/ddsview.cpp create mode 100644 DDSView/ddsview.fx create mode 100644 DDSView/directx.ico create mode 100644 DDSView/hlsl.cmd create mode 100644 DDSView/shaders/ps1D.h create mode 100644 DDSView/shaders/ps1Darray.h create mode 100644 DDSView/shaders/ps2D.h create mode 100644 DDSView/shaders/ps2Darray.h create mode 100644 DDSView/shaders/ps3D.h create mode 100644 DDSView/shaders/psCube.h create mode 100644 DDSView/shaders/vs.h create mode 100644 DirectXTex/BC.cpp create mode 100644 DirectXTex/BC.h create mode 100644 DirectXTex/BC4BC5.cpp create mode 100644 DirectXTex/BC6HBC7.cpp create mode 100644 DirectXTex/DDS.h create mode 100644 DirectXTex/DirectXTex.h create mode 100644 DirectXTex/DirectXTex.inl create mode 100644 DirectXTex/DirectXTexCompress.cpp create mode 100644 DirectXTex/DirectXTexConvert.cpp create mode 100644 DirectXTex/DirectXTexD3D11.cpp create mode 100644 DirectXTex/DirectXTexDDS.cpp create mode 100644 DirectXTex/DirectXTexFlipRotate.cpp create mode 100644 DirectXTex/DirectXTexImage.cpp create mode 100644 DirectXTex/DirectXTexMipmaps.cpp create mode 100644 DirectXTex/DirectXTexMisc.cpp create mode 100644 DirectXTex/DirectXTexNormalMaps.cpp create mode 100644 DirectXTex/DirectXTexP.h create mode 100644 DirectXTex/DirectXTexResize.cpp create mode 100644 DirectXTex/DirectXTexTGA.cpp create mode 100644 DirectXTex/DirectXTexUtil.cpp create mode 100644 DirectXTex/DirectXTexWIC.cpp create mode 100644 DirectXTex/DirectXTex_11_Desktop.sln create mode 100644 DirectXTex/DirectXTex_11_Desktop.vcxproj create mode 100644 DirectXTex/DirectXTex_11_Desktop.vcxproj.filters create mode 100644 DirectXTex/DirectXTex_2010.sln create mode 100644 DirectXTex/DirectXTex_2010.vcxproj create mode 100644 DirectXTex/DirectXTex_2010.vcxproj.filters create mode 100644 DirectXTex/scoped.h create mode 100644 Texconv/Texconv.rc create mode 100644 Texconv/Texconv_11_Desktop.sln create mode 100644 Texconv/Texconv_11_Desktop.vcxproj create mode 100644 Texconv/Texconv_11_Desktop.vcxproj.filters create mode 100644 Texconv/directx.ico create mode 100644 Texconv/texconv.cpp create mode 100644 Texconv/texconv_2010.sln create mode 100644 Texconv/texconv_2010.vcxproj create mode 100644 Texconv/texconv_2010.vcxproj.filters create mode 100644 WICTextureLoader/WICTextureLoader.cpp create mode 100644 WICTextureLoader/WICTextureLoader.h diff --git a/DDSTextureLoader/DDSTextureLoader.cpp b/DDSTextureLoader/DDSTextureLoader.cpp new file mode 100644 index 0000000..d59fbc0 --- /dev/null +++ b/DDSTextureLoader/DDSTextureLoader.cpp @@ -0,0 +1,1413 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader.cpp +// +// Function for loading a DDS texture and creating a Direct3D 11 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. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "DDSTextureLoader.h" + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) && !defined(DXGI_1_2_FORMATS) +#define DXGI_1_2_FORMATS +#endif + +//-------------------------------------------------------------------------------------- +// 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) + +#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_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 + +#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_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT +#define DDS_WIDTH 0x00000004 // DDSD_WIDTH + +#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE +#define DDS_SURFACE_FLAGS_MIPMAP 0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX + +#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 + +#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME + +typedef struct +{ + 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; +} DDS_HEADER; + +typedef struct +{ + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see D3D11_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t reserved; +} DDS_HEADER_DXT10; + +#pragma pack(pop) + +//--------------------------------------------------------------------------------- +struct handle_closer { void operator()(HANDLE h) { if (h) CloseHandle(h); } }; + +typedef public std::unique_ptr ScopedHandle; + +inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; } + +//-------------------------------------------------------------------------------------- +static HRESULT LoadTextureDataFromFile( _In_z_ const wchar_t* fileName, + std::unique_ptr& ddsData, + DDS_HEADER** header, + uint8_t** bitData, + size_t* bitSize + ) +{ + if (!header || !bitData || !bitSize) + { + return E_POINTER; + } + + // open the file +#if (_WIN32_WINNT >= 0x0602 /*_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 + LARGE_INTEGER FileSize = { 0 }; + +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) + FILE_STANDARD_INFO fileInfo; + if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + FileSize = fileInfo.EndOfFile; +#else + GetFileSizeEx( hFile.get(), &FileSize ); +#endif + + // File is too big for 32-bit allocation, so reject read + if (FileSize.HighPart > 0) + { + return E_FAIL; + } + + // Need at least enough data to fill the header and magic number to be a valid DDS + if (FileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) ) + { + return E_FAIL; + } + + // create enough space for the file data + ddsData.reset( new uint8_t[ FileSize.LowPart ] ); + if (!ddsData ) + { + return E_OUTOFMEMORY; + } + + // read the data in + DWORD BytesRead = 0; + if (!ReadFile( hFile.get(), + ddsData.get(), + FileSize.LowPart, + &BytesRead, + nullptr + )) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if (BytesRead < FileSize.LowPart) + { + return E_FAIL; + } + + // DDS files always start with the same magic number ("DDS ") + uint32_t dwMagicNumber = *( const uint32_t* )( ddsData.get() ); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + DDS_HEADER* 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 + bool bDXT10Header = false; + if ((hdr->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC( 'D', 'X', '1', '0' ) == hdr->ddspf.fourCC)) + { + // Must be long enough for both headers and magic value + if (FileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10) ) ) + { + return E_FAIL; + } + + bDXT10Header = true; + } + + // setup the pointers in the process request + *header = hdr; + ptrdiff_t offset = sizeof( uint32_t ) + sizeof( DDS_HEADER ) + + (bDXT10Header ? sizeof( DDS_HEADER_DXT10 ) : 0); + *bitData = ddsData.get() + offset; + *bitSize = FileSize.LowPart - offset; + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +// Return the BPP for a particular format +//-------------------------------------------------------------------------------------- +static size_t BitsPerPixel( _In_ DXGI_FORMAT fmt ) +{ + switch( fmt ) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return 32; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: +#endif + return 16; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + return 8; + + case DXGI_FORMAT_R1_UNORM: + return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return 8; + + default: + return 0; + } +} + + +//-------------------------------------------------------------------------------------- +// Get surface information for a particular format +//-------------------------------------------------------------------------------------- +static void GetSurfaceInfo( _In_ size_t width, + _In_ size_t height, + _In_ DXGI_FORMAT fmt, + _Out_opt_ size_t* outNumBytes, + _Out_opt_ size_t* outRowBytes, + _Out_opt_ size_t* outNumRows ) +{ + size_t numBytes = 0; + size_t rowBytes = 0; + size_t numRows = 0; + + bool bc = false; + bool packed = false; + size_t bcnumBytesPerBlock = 0; + switch (fmt) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + bc=true; + bcnumBytesPerBlock = 8; + break; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + bc = true; + bcnumBytesPerBlock = 16; + break; + + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + packed = true; + break; + } + + if (bc) + { + size_t numBlocksWide = 0; + if (width > 0) + { + numBlocksWide = std::max( 1, (width + 3) / 4 ); + } + size_t numBlocksHigh = 0; + if (height > 0) + { + numBlocksHigh = std::max( 1, (height + 3) / 4 ); + } + rowBytes = numBlocksWide * bcnumBytesPerBlock; + numRows = numBlocksHigh; + } + else if (packed) + { + rowBytes = ( ( width + 1 ) >> 1 ) * 4; + numRows = height; + } + else + { + size_t bpp = BitsPerPixel( fmt ); + rowBytes = ( width * bpp + 7 ) / 8; // round up to nearest byte + numRows = height; + } + + numBytes = rowBytes * numRows; + if (outNumBytes) + { + *outNumBytes = numBytes; + } + if (outRowBytes) + { + *outRowBytes = rowBytes; + } + if (outNumRows) + { + *outNumRows = numRows; + } +} + + +//-------------------------------------------------------------------------------------- +#define ISBITMASK( r,g,b,a ) ( ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a ) + +static DXGI_FORMAT GetDXGIFormat( const DDS_PIXELFORMAT& ddpf ) +{ + if (ddpf.flags & DDS_RGB) + { + // Note that sRGB formats are written using the "DX10" extended header + + switch (ddpf.RGBBitCount) + { + case 32: + if (ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0xff000000)) + { + return DXGI_FORMAT_B8G8R8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0x00000000)) + { + return DXGI_FORMAT_B8G8R8X8_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0x00000000) 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 assumme + // below that the 'backwards' header mask is being used since it is most + // likely written by D3DX. The more robust solution is to use the 'DX10' + // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly + + // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data + if (ISBITMASK(0x3ff00000,0x000ffc00,0x000003ff,0xc0000000)) + { + return DXGI_FORMAT_R10G10B10A2_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10 + + if (ISBITMASK(0x0000ffff,0xffff0000,0x00000000,0x00000000)) + { + return DXGI_FORMAT_R16G16_UNORM; + } + + if (ISBITMASK(0xffffffff,0x00000000,0x00000000,0x00000000)) + { + // Only 32-bit color channel format in D3D9 was R32F + return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 + } + break; + + case 24: + // No 24bpp DXGI formats aka D3DFMT_R8G8B8 + break; + + case 16: + if (ISBITMASK(0x7c00,0x03e0,0x001f,0x8000)) + { + return DXGI_FORMAT_B5G5R5A1_UNORM; + } + if (ISBITMASK(0xf800,0x07e0,0x001f,0x0000)) + { + return DXGI_FORMAT_B5G6R5_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0x0000) aka D3DFMT_X1R5G5B5 + +#ifdef DXGI_1_2_FORMATS + if (ISBITMASK(0x0f00,0x00f0,0x000f,0xf000)) + { + return DXGI_FORMAT_B4G4R4A4_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x0f00,0x00f0,0x000f,0x0000) aka D3DFMT_X4R4G4B4 +#endif + + // No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc. + break; + } + } + else if (ddpf.flags & DDS_LUMINANCE) + { + if (8 == ddpf.RGBBitCount) + { + if (ISBITMASK(0x000000ff,0x00000000,0x00000000,0x00000000)) + { + 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 (16 == ddpf.RGBBitCount) + { + if (ISBITMASK(0x0000ffff,0x00000000,0x00000000,0x00000000)) + { + return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x000000ff,0x00000000,0x00000000,0x0000ff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + } + } + else if (ddpf.flags & DDS_ALPHA) + { + if (8 == ddpf.RGBBitCount) + { + return DXGI_FORMAT_A8_UNORM; + } + } + else if (ddpf.flags & DDS_FOURCC) + { + if (MAKEFOURCC( 'D', 'X', 'T', '1' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC1_UNORM; + } + if (MAKEFOURCC( 'D', 'X', 'T', '3' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC( 'D', 'X', 'T', '5' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + // While pre-mulitplied alpha isn't directly supported by the DXGI formats, + // they are basically the same as these BC formats so they can be mapped + if (MAKEFOURCC( 'D', 'X', 'T', '2' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC( 'D', 'X', 'T', '4' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + if (MAKEFOURCC( 'A', 'T', 'I', '1' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC( 'B', 'C', '4', 'U' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC( 'B', 'C', '4', 'S' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_SNORM; + } + + if (MAKEFOURCC( 'A', 'T', 'I', '2' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC( 'B', 'C', '5', 'U' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC( 'B', 'C', '5', 'S' ) == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_SNORM; + } + + // BC6H and BC7 are written using the "DX10" extended header + + if (MAKEFOURCC( 'R', 'G', 'B', 'G' ) == ddpf.fourCC) + { + return DXGI_FORMAT_R8G8_B8G8_UNORM; + } + if (MAKEFOURCC( 'G', 'R', 'G', 'B' ) == ddpf.fourCC) + { + return DXGI_FORMAT_G8R8_G8B8_UNORM; + } + + // Check for D3DFORMAT enums being set here + switch( ddpf.fourCC ) + { + case 36: // D3DFMT_A16B16G16R16 + return DXGI_FORMAT_R16G16B16A16_UNORM; + + case 110: // D3DFMT_Q16W16V16U16 + return DXGI_FORMAT_R16G16B16A16_SNORM; + + case 111: // D3DFMT_R16F + return DXGI_FORMAT_R16_FLOAT; + + case 112: // D3DFMT_G16R16F + return DXGI_FORMAT_R16G16_FLOAT; + + case 113: // D3DFMT_A16B16G16R16F + return DXGI_FORMAT_R16G16B16A16_FLOAT; + + case 114: // D3DFMT_R32F + return DXGI_FORMAT_R32_FLOAT; + + case 115: // D3DFMT_G32R32F + return DXGI_FORMAT_R32G32_FLOAT; + + case 116: // D3DFMT_A32B32G32R32F + return DXGI_FORMAT_R32G32B32A32_FLOAT; + } + } + + return DXGI_FORMAT_UNKNOWN; +} + + +//-------------------------------------------------------------------------------------- +static HRESULT FillInitData( _In_ size_t width, + _In_ size_t height, + _In_ size_t depth, + _In_ size_t mipCount, + _In_ size_t arraySize, + _In_ DXGI_FORMAT format, + _In_ size_t maxsize, + _In_ size_t bitSize, + _In_bytecount_(bitSize) const uint8_t* bitData, + _Out_ size_t& twidth, + _Out_ size_t& theight, + _Out_ size_t& tdepth, + _Out_ size_t& skipMip, + _Out_cap_(mipCount*arraySize) D3D11_SUBRESOURCE_DATA* initData ) +{ + if ( !bitData || !initData ) + return E_POINTER; + + skipMip = 0; + twidth = 0; + theight = 0; + tdepth = 0; + + size_t NumBytes = 0; + size_t RowBytes = 0; + size_t NumRows = 0; + const uint8_t* pSrcBits = bitData; + const uint8_t* pEndBits = bitData + bitSize; + + size_t index = 0; + for( size_t j = 0; j < arraySize; j++ ) + { + size_t w = width; + size_t h = height; + size_t d = depth; + for( size_t i = 0; i < mipCount; i++ ) + { + GetSurfaceInfo( w, + h, + format, + &NumBytes, + &RowBytes, + &NumRows + ); + + if ( (mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize) ) + { + if ( !twidth ) + { + twidth = w; + theight = h; + tdepth = d; + } + + initData[index].pSysMem = ( const void* )pSrcBits; + initData[index].SysMemPitch = static_cast( RowBytes ); + initData[index].SysMemSlicePitch = static_cast( NumBytes ); + ++index; + } + else + ++skipMip; + + if (pSrcBits + (NumBytes*d) > pEndBits) + { + return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF ); + } + + pSrcBits += NumBytes * d; + + w = w >> 1; + h = h >> 1; + d = d >> 1; + if (w == 0) + { + w = 1; + } + if (h == 0) + { + h = 1; + } + if (d == 0) + { + d = 1; + } + } + } + + return (index > 0) ? S_OK : E_FAIL; +} + + +//-------------------------------------------------------------------------------------- +static HRESULT CreateD3DResources( _In_ ID3D11Device* d3dDevice, + _In_ uint32_t resDim, + _In_ size_t width, + _In_ size_t height, + _In_ size_t depth, + _In_ size_t mipCount, + _In_ size_t arraySize, + _In_ DXGI_FORMAT format, + _In_ bool isCubeMap, + _In_count_(mipCount*arraySize) D3D11_SUBRESOURCE_DATA* initData, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView ) +{ + if ( !d3dDevice || !initData ) + return E_POINTER; + + HRESULT hr = E_FAIL; + + switch ( resDim ) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + { + D3D11_TEXTURE1D_DESC desc; + desc.Width = static_cast( width ); + desc.MipLevels = static_cast( mipCount ); + desc.ArraySize = static_cast( arraySize ); + desc.Format = format; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + ID3D11Texture1D* tex = nullptr; + hr = d3dDevice->CreateTexture1D( &desc, + initData, + &tex + ); + if (SUCCEEDED( hr ) && tex != 0) + { + if (textureView != 0) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + memset( &SRVDesc, 0, sizeof( SRVDesc ) ); + SRVDesc.Format = format; + + if (arraySize > 1) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; + SRVDesc.Texture1DArray.MipLevels = desc.MipLevels; + SRVDesc.Texture1DArray.ArraySize = static_cast( arraySize ); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; + SRVDesc.Texture1D.MipLevels = desc.MipLevels; + } + + hr = d3dDevice->CreateShaderResourceView( tex, + &SRVDesc, + textureView + ); + if ( FAILED(hr) ) + { + tex->Release(); + return hr; + } + } + + if (texture != 0) + { + *texture = tex; + } + else + { +#if defined(DEBUG) || defined(PROFILE) + tex->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("DDSTextureLoader")-1, + "DDSTextureLoader" ); +#endif + tex->Release(); + } + } + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + { + D3D11_TEXTURE2D_DESC desc; + desc.Width = static_cast( width ); + desc.Height = static_cast( height ); + desc.MipLevels = static_cast( mipCount ); + desc.ArraySize = static_cast( arraySize ); + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = (isCubeMap) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + + ID3D11Texture2D* tex = nullptr; + hr = d3dDevice->CreateTexture2D( &desc, + initData, + &tex + ); + if (SUCCEEDED( hr ) && tex != 0) + { + if (textureView != 0) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + memset( &SRVDesc, 0, sizeof( SRVDesc ) ); + SRVDesc.Format = format; + + if (isCubeMap) + { + if (arraySize > 6) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + SRVDesc.TextureCubeArray.MipLevels = desc.MipLevels; + + // Earlier we set arraySize to (NumCubes * 6) + SRVDesc.TextureCubeArray.NumCubes = static_cast( arraySize / 6 ); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + SRVDesc.TextureCube.MipLevels = desc.MipLevels; + } + } + else if (arraySize > 1) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + SRVDesc.Texture2DArray.MipLevels = desc.MipLevels; + SRVDesc.Texture2DArray.ArraySize = static_cast( arraySize ); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + SRVDesc.Texture2D.MipLevels = desc.MipLevels; + } + + hr = d3dDevice->CreateShaderResourceView( tex, + &SRVDesc, + textureView + ); + if ( FAILED(hr) ) + { + tex->Release(); + return hr; + } + } + + if (texture != 0) + { + *texture = tex; + } + else + { +#if defined(DEBUG) || defined(PROFILE) + tex->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("DDSTextureLoader")-1, + "DDSTextureLoader" + ); +#endif + tex->Release(); + } + } + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + { + D3D11_TEXTURE3D_DESC desc; + desc.Width = static_cast( width ); + desc.Height = static_cast( height ); + desc.Depth = static_cast( depth ); + desc.MipLevels = static_cast( mipCount ); + desc.Format = format; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + ID3D11Texture3D* tex = nullptr; + hr = d3dDevice->CreateTexture3D( &desc, + initData, + &tex + ); + if (SUCCEEDED( hr ) && tex != 0) + { + if (textureView != 0) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + memset( &SRVDesc, 0, sizeof( SRVDesc ) ); + SRVDesc.Format = format; + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + SRVDesc.Texture3D.MipLevels = desc.MipLevels; + + hr = d3dDevice->CreateShaderResourceView( tex, + &SRVDesc, + textureView + ); + if ( FAILED(hr) ) + { + tex->Release(); + return hr; + } + } + + if (texture != 0) + { + *texture = tex; + } + else + { +#if defined(DEBUG) || defined(PROFILE) + tex->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("DDSTextureLoader")-1, + "DDSTextureLoader" ); +#endif + tex->Release(); + } + } + } + break; + } + + return hr; +} + + +//-------------------------------------------------------------------------------------- +static HRESULT CreateTextureFromDDS( _In_ ID3D11Device* d3dDevice, + _In_ const DDS_HEADER* header, + _In_bytecount_(bitSize) const uint8_t* bitData, + _In_ size_t bitSize, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize ) +{ + HRESULT hr = S_OK; + + size_t width = header->width; + size_t height = header->height; + size_t depth = header->depth; + + uint32_t resDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; + size_t arraySize = 1; + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + bool isCubeMap = false; + + size_t mipCount = header->mipMapCount; + if (0 == mipCount) + { + mipCount = 1; + } + + if ((header->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC( 'D', 'X', '1', '0' ) == header->ddspf.fourCC )) + { + const DDS_HEADER_DXT10* d3d10ext = reinterpret_cast( (const char*)header + sizeof(DDS_HEADER) ); + + arraySize = d3d10ext->arraySize; + if (arraySize == 0) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + if (BitsPerPixel( d3d10ext->dxgiFormat ) == 0) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + format = d3d10ext->dxgiFormat; + + switch ( d3d10ext->resourceDimension ) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + // D3DX writes 1D textures with a fixed Height of 1 + if ((header->flags & DDS_HEIGHT) && height != 1) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + height = depth = 1; + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + if (d3d10ext->miscFlag & D3D11_RESOURCE_MISC_TEXTURECUBE) + { + arraySize *= 6; + isCubeMap = true; + } + depth = 1; + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + if (arraySize > 1) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + break; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + resDim = d3d10ext->resourceDimension; + } + else + { + format = GetDXGIFormat( header->ddspf ); + + if (format == DXGI_FORMAT_UNKNOWN) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + if (header->flags & DDS_HEADER_FLAGS_VOLUME) + { + resDim = D3D11_RESOURCE_DIMENSION_TEXTURE3D; + } + else + { + if (header->caps2 & DDS_CUBEMAP) + { + // We require all six faces to be defined + if ((header->caps2 & DDS_CUBEMAP_ALLFACES ) != DDS_CUBEMAP_ALLFACES) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + arraySize = 6; + isCubeMap = true; + } + + depth = 1; + resDim = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + + // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture + } + + assert( BitsPerPixel( format ) != 0 ); + } + + // Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 11.x hardware requirements) + if (mipCount > D3D11_REQ_MIP_LEVELS) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + switch ( resDim ) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || + (width > D3D11_REQ_TEXTURE1D_U_DIMENSION) ) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + if (isCubeMap) + { + // This is the right bound because we set arraySize to (NumCubes*6) above + if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D11_REQ_TEXTURECUBE_DIMENSION) || + (height > D3D11_REQ_TEXTURECUBE_DIMENSION)) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + } + else if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) || + (height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + if ((arraySize > 1) || + (width > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (height > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + break; + } + + // Create the texture + std::unique_ptr initData( new D3D11_SUBRESOURCE_DATA[ mipCount * arraySize ] ); + if ( !initData ) + { + return E_OUTOFMEMORY; + } + + size_t skipMip = 0; + size_t twidth = 0; + size_t theight = 0; + size_t tdepth = 0; + hr = FillInitData( width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, + twidth, theight, tdepth, skipMip, initData.get() ); + + if ( SUCCEEDED(hr) ) + { + hr = CreateD3DResources( d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, isCubeMap, initData.get(), texture, textureView ); + + if ( (hr == E_INVALIDARG) && !maxsize && (mipCount > 1) ) + { + // Retry with a maxsize determined by feature level + switch( d3dDevice->GetFeatureLevel() ) + { + case D3D_FEATURE_LEVEL_9_1: + case D3D_FEATURE_LEVEL_9_2: + if (isCubeMap) + { + maxsize = 512 /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/; + } + else + { + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + } + break; + + case D3D_FEATURE_LEVEL_9_3: + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + break; + + default: // D3D_FEATURE_LEVEL_10_0 & D3D_FEATURE_LEVEL_10_1 + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? 2048 /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/ + : 8192 /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + break; + } + + hr = FillInitData( width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, + twidth, theight, tdepth, skipMip, initData.get() ); + if ( SUCCEEDED(hr) ) + { + hr = CreateD3DResources( d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, isCubeMap, initData.get(), texture, textureView ); + } + } + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +HRESULT CreateDDSTextureFromMemory( _In_ ID3D11Device* d3dDevice, + _In_bytecount_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize ) +{ + if (!d3dDevice || !ddsData || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + // Validate DDS file in memory + if (ddsDataSize < (sizeof(uint32_t) + sizeof(DDS_HEADER))) + { + return E_FAIL; + } + + uint32_t dwMagicNumber = *( const uint32_t* )( ddsData ); + if (dwMagicNumber != DDS_MAGIC) + { + return E_FAIL; + } + + const DDS_HEADER* header = reinterpret_cast( ddsData + sizeof( uint32_t ) ); + + // Verify header to validate DDS file + if (header->size != sizeof(DDS_HEADER) || + header->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + return E_FAIL; + } + + // Check for DX10 extension + bool bDXT10Header = false; + if ((header->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC( 'D', 'X', '1', '0' ) == header->ddspf.fourCC) ) + { + // Must be long enough for both headers and magic value + if (ddsDataSize < (sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10))) + { + return E_FAIL; + } + + bDXT10Header = true; + } + + ptrdiff_t offset = sizeof( uint32_t ) + + sizeof( DDS_HEADER ) + + (bDXT10Header ? sizeof( DDS_HEADER_DXT10 ) : 0); + + HRESULT hr = CreateTextureFromDDS( d3dDevice, + header, + ddsData + offset, + ddsDataSize - offset, + texture, + textureView, + maxsize + ); + +#if defined(DEBUG) || defined(PROFILE) + if (texture != 0 && *texture != 0) + { + (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("DDSTextureLoader")-1, + "DDSTextureLoader" + ); + } + + if (textureView != 0 && *textureView != 0) + { + (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("DDSTextureLoader")-1, + "DDSTextureLoader" + ); + } +#endif + + return hr; +} + +//-------------------------------------------------------------------------------------- +HRESULT CreateDDSTextureFromFile( _In_ ID3D11Device* d3dDevice, + _In_z_ const wchar_t* fileName, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize ) +{ + if (!d3dDevice || !fileName || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + DDS_HEADER* header = nullptr; + 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; + } + + hr = CreateTextureFromDDS( d3dDevice, + header, + bitData, + bitSize, + texture, + textureView, + maxsize + ); + +#if defined(DEBUG) || defined(PROFILE) + if (texture != 0 || textureView != 0) + { + CHAR strFileA[MAX_PATH]; + WideCharToMultiByte( CP_ACP, + WC_NO_BEST_FIT_CHARS, + fileName, + -1, + strFileA, + MAX_PATH, + nullptr, + FALSE + ); + const CHAR* pstrName = strrchr( strFileA, '\\' ); + if (!pstrName) + { + pstrName = strFileA; + } + else + { + pstrName++; + } + + if (texture != 0 && *texture != 0) + { + (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, + lstrlenA(pstrName), + pstrName + ); + } + + if (textureView != 0 && *textureView != 0 ) + { + (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, + lstrlenA(pstrName), + pstrName + ); + } + } +#endif + + return hr; +} diff --git a/DDSTextureLoader/DDSTextureLoader.h b/DDSTextureLoader/DDSTextureLoader.h new file mode 100644 index 0000000..b6732e8 --- /dev/null +++ b/DDSTextureLoader/DDSTextureLoader.h @@ -0,0 +1,45 @@ +//-------------------------------------------------------------------------------------- +// File: DDSTextureLoader.h +// +// Function for loading a DDS texture and creating a Direct3D 11 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. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#pragma warning(push) +#pragma warning(disable : 4005) +#include +#pragma warning(pop) + +HRESULT CreateDDSTextureFromMemory( _In_ ID3D11Device* d3dDevice, + _In_bytecount_(ddsDataSize) const uint8_t* ddsData, + _In_ size_t ddsDataSize, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0 + ); + +HRESULT CreateDDSTextureFromFile( _In_ ID3D11Device* d3dDevice, + _In_z_ const wchar_t* szFileName, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0 + ); diff --git a/DDSView/DDSView.rc b/DDSView/DDSView.rc new file mode 100644 index 0000000..091de1c --- /dev/null +++ b/DDSView/DDSView.rc @@ -0,0 +1,75 @@ +// Microsoft Visual C++ generated resource script. +// +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define IDC_STATIC -1 +#define IDI_MAIN_ICON 100 +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAIN_ICON ICON "directx.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define IDC_STATIC -1\r\n" + "#include \r\n" + "\r\n" + "\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/DDSView/DDSView_11_Desktop.sln b/DDSView/DDSView_11_Desktop.sln new file mode 100644 index 0000000..4be0080 --- /dev/null +++ b/DDSView/DDSView_11_Desktop.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DDSView", "DDSView_11_Desktop.vcxproj", "{9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTex", "..\DirectXTex\DirectXTex_11_Desktop.vcxproj", "{371B9FA9-4C90-4AC6-A123-ACED756D6C77}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Profile|Win32 = Profile|Win32 + Profile|x64 = Profile|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|Win32.Build.0 = Debug|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|x64.ActiveCfg = Debug|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|x64.Build.0 = Debug|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|Win32.ActiveCfg = Profile|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|Win32.Build.0 = Profile|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|x64.ActiveCfg = Profile|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|x64.Build.0 = Profile|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|Win32.ActiveCfg = Release|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|Win32.Build.0 = Release|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|x64.ActiveCfg = Release|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.Build.0 = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.ActiveCfg = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.ActiveCfg = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.Build.0 = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.ActiveCfg = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.Build.0 = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.ActiveCfg = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.Build.0 = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/DDSView/DDSView_11_Desktop.vcxproj b/DDSView/DDSView_11_Desktop.vcxproj new file mode 100644 index 0000000..c783451 --- /dev/null +++ b/DDSView/DDSView_11_Desktop.vcxproj @@ -0,0 +1,390 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Profile + Win32 + + + Profile + x64 + + + Release + Win32 + + + Release + x64 + + + + DDSView + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84} + DDSView + Win32Proj + $(VCTargetsPath11) + + + + Application + Unicode + v110 + + + Application + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + + + + + + + + + + + + + + + + + + + + + + true + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + true + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + EditAndContinue + EnableFastChecks + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Windows + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + EnableFastChecks + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Windows + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + + + + + + + {371b9fa9-4c90-4ac6-a123-aced756d6c77} + + + + + + + + \ No newline at end of file diff --git a/DDSView/DDSView_11_Desktop.vcxproj.filters b/DDSView/DDSView_11_Desktop.vcxproj.filters new file mode 100644 index 0000000..e546d45 --- /dev/null +++ b/DDSView/DDSView_11_Desktop.vcxproj.filters @@ -0,0 +1,20 @@ + + + + + {8e114980-c1a3-4ada-ad7c-83caadf5daeb} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + + + + Resource Files + + + + + + \ No newline at end of file diff --git a/DDSView/DDSView_2010.sln b/DDSView/DDSView_2010.sln new file mode 100644 index 0000000..947aa2c --- /dev/null +++ b/DDSView/DDSView_2010.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DDSView", "DDSView_2010.vcxproj", "{9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTex", "..\DirectXTex\DirectXTex_2010.vcxproj", "{371B9FA9-4C90-4AC6-A123-ACED756D6C77}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Profile|Win32 = Profile|Win32 + Profile|x64 = Profile|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|Win32.Build.0 = Debug|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|x64.ActiveCfg = Debug|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Debug|x64.Build.0 = Debug|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|Win32.ActiveCfg = Profile|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|Win32.Build.0 = Profile|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|x64.ActiveCfg = Profile|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Profile|x64.Build.0 = Profile|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|Win32.ActiveCfg = Release|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|Win32.Build.0 = Release|Win32 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|x64.ActiveCfg = Release|x64 + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84}.Release|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.Build.0 = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.ActiveCfg = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.ActiveCfg = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.Build.0 = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.ActiveCfg = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.Build.0 = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.ActiveCfg = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.Build.0 = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/DDSView/DDSView_2010.vcxproj b/DDSView/DDSView_2010.vcxproj new file mode 100644 index 0000000..0874ddc --- /dev/null +++ b/DDSView/DDSView_2010.vcxproj @@ -0,0 +1,383 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Profile + Win32 + + + Profile + x64 + + + Release + Win32 + + + Release + x64 + + + + DDSView + {9D3EDCAD-A800-43F0-B77F-FE6E4DFA3D84} + DDSView + Win32Proj + + + + Application + Unicode + + + Application + Unicode + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + true + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + true + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + EditAndContinue + EnableFastChecks + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Windows + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + EnableFastChecks + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Windows + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + d3d11.lib;ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + + + + + + + {371b9fa9-4c90-4ac6-a123-aced756d6c77} + + + + + + + + \ No newline at end of file diff --git a/DDSView/DDSView_2010.vcxproj.filters b/DDSView/DDSView_2010.vcxproj.filters new file mode 100644 index 0000000..e546d45 --- /dev/null +++ b/DDSView/DDSView_2010.vcxproj.filters @@ -0,0 +1,20 @@ + + + + + {8e114980-c1a3-4ada-ad7c-83caadf5daeb} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + + + + Resource Files + + + + + + \ No newline at end of file diff --git a/DDSView/ddsview.cpp b/DDSView/ddsview.cpp new file mode 100644 index 0000000..0db7bdc --- /dev/null +++ b/DDSView/ddsview.cpp @@ -0,0 +1,736 @@ +//-------------------------------------------------------------------------------------- +// File: DDSView.cpp +// +// DirectX 11 DDS File Viewer +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "xnamath.h" + +#include "DirectXTex.h" + +using namespace DirectX; + +//-------------------------------------------------------------------------------------- +#define IDI_MAIN_ICON 100 + +//-------------------------------------------------------------------------------------- +#pragma pack(push,1) +struct SimpleVertex +{ + XMFLOAT4 Pos; + XMFLOAT4 Tex; +}; + +struct CBArrayControl +{ + float Index; + float pad[3]; +}; +#pragma pack(pop) + +//-------------------------------------------------------------------------------------- + +// fxc ddsview.fx /nologo /EVS /Tvs_4_1 /Fhshaders\vs.h +#include "shaders\vs.h" + +// fxc ddsview.fx /nologo /EPS_1D /Tps_4_1 /Fhshaders\ps1D.h +#include "shaders\ps1D.h" + +// fxc ddsview.fx /nologo /EPS_1DArray /Tps_4_1 /Fhshaders\ps1Darray.h +#include "shaders\\ps1Darray.h" + +// fxc ddsview.fx /nologo /EPS_2D /Tps_4_1 /Fhshaders\ps2D.h +#include "shaders\\ps2D.h" + +// fxc ddsview.fx /nologo /EPS_2DArray /Tps_4_1 /Fhshaders\ps2Darray.h +#include "shaders\ps2Darray.h" + +// fxc ddsview.fx /nologo /EPS_3D /Tps_4_1 /Fhshaders\ps3D.h +#include "shaders\ps3D.h" + +// fxc ddsview.fx /nologo /EPS_Cube /Tps_4_1 /Fhshaders\psCube.h +#include "shaders\psCube.h" + +//-------------------------------------------------------------------------------------- +HINSTANCE g_hInst = NULL; +HWND g_hWnd = NULL; +D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL; +D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0; +ID3D11Device* g_pd3dDevice = NULL; +ID3D11DeviceContext* g_pImmediateContext = NULL; +IDXGISwapChain* g_pSwapChain = NULL; +ID3D11RenderTargetView* g_pRenderTargetView = NULL; +ID3D11Texture2D* g_pDepthStencil = NULL; +ID3D11DepthStencilView* g_pDepthStencilView = NULL; +ID3D11VertexShader* g_pVertexShader = NULL; +ID3D11PixelShader* g_pPixelShader = NULL; +ID3D11InputLayout* g_pVertexLayout = NULL; +ID3D11Buffer* g_pVertexBuffer = NULL; +ID3D11Buffer* g_pIndexBuffer = NULL; +ID3D11Buffer* g_pCBArrayControl = NULL; +ID3D11ShaderResourceView* g_pSRV = NULL; +ID3D11BlendState* g_AlphaBlendState = NULL; +ID3D11SamplerState* g_pSamplerLinear = NULL; + +UINT g_iCurrentIndex = 0; +UINT g_iMaxIndex = 1; + +UINT g_iIndices = 0; + + +//-------------------------------------------------------------------------------------- +HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow, const TexMetadata& mdata ); +HRESULT InitDevice( const TexMetadata& mdata ); +void CleanupDevice(); +LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); +void Render(); + +//-------------------------------------------------------------------------------------- +int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) +{ + UNREFERENCED_PARAMETER( hPrevInstance ); + UNREFERENCED_PARAMETER( lpCmdLine ); + + if ( !*lpCmdLine ) + { + MessageBox( NULL, L"Usage: ddsview ", L"DDSView", MB_OK | MB_ICONEXCLAMATION ); + return 0; + } + + TexMetadata mdata; + HRESULT hr = GetMetadataFromDDSFile( lpCmdLine, DDS_FLAGS_NONE, mdata ); + if ( FAILED(hr) ) + { + WCHAR buff[2048]; + swprintf_s( buff, L"Failed to open texture file\n\nFilename = %s\nHRESULT %08X", lpCmdLine, hr ); + MessageBox( NULL, buff, L"DDSView", MB_OK | MB_ICONEXCLAMATION ); + return 0; + } + + if( FAILED( InitWindow( hInstance, nCmdShow, mdata ) ) ) + return 0; + + if( FAILED( InitDevice( mdata ) ) ) + { + CleanupDevice(); + return 0; + } + + if (mdata.dimension == TEX_DIMENSION_TEXTURE3D) + { + if ( mdata.arraySize > 1 ) + { + WCHAR buff[2048]; + swprintf_s( buff, L"Arrays of volume textures are not supported\n\nFilename = %s\nArray size %d", lpCmdLine, mdata.arraySize ); + MessageBox( NULL, buff, L"DDSView", MB_OK | MB_ICONEXCLAMATION ); + return 0; + } + + g_iMaxIndex = static_cast( mdata.depth ); + } + else + { + g_iMaxIndex = static_cast( mdata.arraySize ); + } + + switch( mdata.format ) + { + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + if ( g_featureLevel < D3D_FEATURE_LEVEL_11_0 ) + { + WCHAR buff[2048]; + swprintf_s( buff, L"BC6H/BC7 requires DirectX 11 hardware\n\nFilename = %s\nDXGI Format %d\nFeature Level %d", lpCmdLine, mdata.format, g_featureLevel ); + MessageBox( NULL, buff, L"DDSView", MB_OK | MB_ICONEXCLAMATION ); + return 0; + } + break; + + default: + { + UINT flags = 0; + hr = g_pd3dDevice->CheckFormatSupport ( mdata.format, &flags ); + if ( FAILED(hr) || !(flags & (D3D11_FORMAT_SUPPORT_TEXTURE1D|D3D11_FORMAT_SUPPORT_TEXTURE2D|D3D11_FORMAT_SUPPORT_TEXTURE3D)) ) + { + WCHAR buff[2048]; + swprintf_s( buff, L"Format not supported by DirectX hardware\n\nFilename = %s\nDXGI Format %d\nFeature Level %d\nHRESULT = %08X", lpCmdLine, mdata.format, g_featureLevel, hr ); + MessageBox( NULL, buff, L"DDSView", MB_OK | MB_ICONEXCLAMATION ); + return 0; + } + } + break; + } + + ScratchImage image; + hr = LoadFromDDSFile( lpCmdLine, DDS_FLAGS_NONE, &mdata, image ); + if ( FAILED(hr) ) + { + WCHAR buff[2048]; + swprintf_s( buff, L"Failed to load texture file\n\nFilename = %s\nHRESULT %08X", lpCmdLine, hr ); + MessageBox( NULL, buff, L"DDSView", MB_OK | MB_ICONEXCLAMATION ); + return 0; + } + + // Special case to make sure Texture cubes remain arrays + mdata.miscFlags &= ~TEX_MISC_TEXTURECUBE; + + hr = CreateShaderResourceView( g_pd3dDevice, image.GetImages(), image.GetImageCount(), mdata, &g_pSRV ); + if ( FAILED(hr) ) + { + WCHAR buff[2048]; + swprintf_s( buff, L"Failed creating texture from file\n\nFilename = %s\nHRESULT = %08X", lpCmdLine, hr ); + MessageBox( NULL, buff, L"DDSView", MB_OK | MB_ICONEXCLAMATION ); + return 0; + } + + // Main message loop + MSG msg = {0}; + while( WM_QUIT != msg.message ) + { + if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + else + { + Render(); + } + } + + CleanupDevice(); + + return ( int )msg.wParam; +} + +//-------------------------------------------------------------------------------------- +HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow, const TexMetadata& mdata ) +{ + // Register class + WNDCLASSEX wcex; + wcex.cbSize = sizeof( WNDCLASSEX ); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon( hInstance, ( LPCTSTR )IDI_MAIN_ICON ); + wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); + wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 ); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = L"DDSViewWindowClass"; + wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_MAIN_ICON ); + if( !RegisterClassEx( &wcex ) ) + return E_FAIL; + + // Create window + g_hInst = hInstance; + RECT rc = { 0, 0, 640, 480 }; + + int cxborder = GetSystemMetrics( SM_CXBORDER ); + int cxedge = GetSystemMetrics( SM_CXEDGE ); + int screenX = GetSystemMetrics( SM_CXSCREEN ) - max( cxborder, cxedge ); + if( rc.right < (LONG)mdata.width ) + rc.right = (LONG)mdata.height; + if ( rc.right > screenX ) + rc.right = screenX; + + int cyborder = GetSystemMetrics( SM_CYBORDER ); + int cyedge = GetSystemMetrics( SM_CYEDGE ); + int screenY = GetSystemMetrics( SM_CYSCREEN ) - max( cyborder, cyedge ); + if ( rc.bottom < (LONG)mdata.height ) + rc.bottom = (LONG)mdata.height; + if ( rc.bottom > screenY ) + rc.bottom = screenY; + + AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE ); + g_hWnd = CreateWindow( L"DDSViewWindowClass", L"DDS View", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, + NULL ); + if( !g_hWnd ) + return E_FAIL; + + ShowWindow( g_hWnd, nCmdShow ); + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + PAINTSTRUCT ps; + HDC hdc; + + switch( message ) + { + case WM_PAINT: + hdc = BeginPaint( hWnd, &ps ); + EndPaint( hWnd, &ps ); + break; + + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + + case WM_KEYDOWN: + if ( wParam == VK_RIGHT ) + { + if ( g_iCurrentIndex < g_iMaxIndex-1 ) + ++g_iCurrentIndex; + } + else if ( wParam == VK_LEFT ) + { + if ( g_iCurrentIndex > 0 ) + { + --g_iCurrentIndex; + } + } + else if ( wParam >= '0' && wParam <= '9' ) + { + UINT index = (wParam == '0') ? 10 : ((UINT) (wParam - '1')); + if ( index < g_iMaxIndex ) + g_iCurrentIndex = index; + } + InvalidateRect( hWnd, NULL, FALSE ); + break; + + default: + return DefWindowProc( hWnd, message, wParam, lParam ); + } + + return 0; +} + + +//-------------------------------------------------------------------------------------- +HRESULT InitDevice( const TexMetadata& mdata ) +{ + HRESULT hr = S_OK; + + RECT rc; + GetClientRect( g_hWnd, &rc ); + UINT width = rc.right - rc.left; + UINT height = rc.bottom - rc.top; + + UINT createDeviceFlags = 0; +#if defined( DEBUG ) || defined( _DEBUG ) + createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + D3D_DRIVER_TYPE driverTypes[] = + { + D3D_DRIVER_TYPE_HARDWARE, + D3D_DRIVER_TYPE_WARP, + D3D_DRIVER_TYPE_REFERENCE, + }; + UINT numDriverTypes = ARRAYSIZE( driverTypes ); + + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + }; + UINT numFeatureLevels = ARRAYSIZE( featureLevels ); + + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory( &sd, sizeof( sd ) ); + sd.BufferCount = 1; + sd.BufferDesc.Width = width; + sd.BufferDesc.Height = height; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.OutputWindow = g_hWnd; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.Windowed = TRUE; + + for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ ) + { + g_driverType = driverTypes[driverTypeIndex]; + hr = D3D11CreateDeviceAndSwapChain( NULL, g_driverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels, + D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext ); + if( SUCCEEDED( hr ) ) + break; + } + if( FAILED( hr ) ) + return hr; + + // Create a render target view + ID3D11Texture2D* pBackBuffer = NULL; + hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer ); + if( FAILED( hr ) ) + return hr; + + hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView ); + pBackBuffer->Release(); + if( FAILED( hr ) ) + return hr; + + // Create depth stencil texture + D3D11_TEXTURE2D_DESC descDepth; + ZeroMemory( &descDepth, sizeof(descDepth) ); + descDepth.Width = width; + descDepth.Height = height; + descDepth.MipLevels = 1; + descDepth.ArraySize = 1; + descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + descDepth.SampleDesc.Count = 1; + descDepth.SampleDesc.Quality = 0; + descDepth.Usage = D3D11_USAGE_DEFAULT; + descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL; + descDepth.CPUAccessFlags = 0; + descDepth.MiscFlags = 0; + hr = g_pd3dDevice->CreateTexture2D( &descDepth, NULL, &g_pDepthStencil ); + if( FAILED( hr ) ) + return hr; + + // Create the depth stencil view + D3D11_DEPTH_STENCIL_VIEW_DESC descDSV; + ZeroMemory( &descDSV, sizeof(descDSV) ); + descDSV.Format = descDepth.Format; + descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + descDSV.Texture2D.MipSlice = 0; + hr = g_pd3dDevice->CreateDepthStencilView( g_pDepthStencil, &descDSV, &g_pDepthStencilView ); + if( FAILED( hr ) ) + return hr; + + g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, g_pDepthStencilView ); + + // Setup the viewport + D3D11_VIEWPORT vp; + vp.Width = (FLOAT)width; + vp.Height = (FLOAT)height; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + g_pImmediateContext->RSSetViewports( 1, &vp ); + + // Create the vertex shader + hr = g_pd3dDevice->CreateVertexShader( g_VS, sizeof(g_VS), NULL, &g_pVertexShader ); + if( FAILED( hr ) ) + return hr; + + // Define the input layout + D3D11_INPUT_ELEMENT_DESC layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, sizeof(XMFLOAT4), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + UINT numElements = ARRAYSIZE( layout ); + + // Create the input layout + hr = g_pd3dDevice->CreateInputLayout( layout, numElements, g_VS, sizeof(g_VS), &g_pVertexLayout ); + if( FAILED( hr ) ) + return hr; + + // Set the input layout + g_pImmediateContext->IASetInputLayout( g_pVertexLayout ); + + // Select the pixel shader + bool isCubeMap = false; + bool is1D = false; + const BYTE* pshader = NULL; + size_t pshader_size = 0; + + switch ( mdata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + if ( mdata.arraySize > 1) + { + pshader = g_PS_1DArray; + pshader_size = sizeof(g_PS_1DArray); + } + else + { + pshader = g_PS_1D; + pshader_size = sizeof(g_PS_1D); + } + is1D = true; + break; + + case TEX_DIMENSION_TEXTURE2D: + if ( mdata.miscFlags & TEX_MISC_TEXTURECUBE ) + { + pshader = g_PS_Cube; + pshader_size = sizeof(g_PS_Cube); + isCubeMap = true; + } + else if ( mdata.arraySize > 1 ) + { + pshader = g_PS_2DArray; + pshader_size = sizeof(g_PS_2DArray); + } + else + { + pshader = g_PS_2D; + pshader_size = sizeof(g_PS_2D); + } + break; + + case TEX_DIMENSION_TEXTURE3D: + pshader = g_PS_3D; + pshader_size = sizeof(g_PS_3D); + break; + + default: + return E_FAIL; + } + + assert( pshader && pshader_size > 0 ); + + // Create the pixel shader + hr = g_pd3dDevice->CreatePixelShader( pshader, pshader_size, NULL, &g_pPixelShader ); + if( FAILED( hr ) ) + return hr; + + // Create vertex buffer + UINT nverts; + D3D11_SUBRESOURCE_DATA InitData; + ZeroMemory( &InitData, sizeof(InitData) ); + + static const SimpleVertex verticesCube[] = + { + // Render cubemaps as horizontal cross + + // XPOS + { XMFLOAT4( .5f, .25f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 0.f, 0.f ) }, + { XMFLOAT4( 1.f, .25f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 0.f, 0.f ) }, + { XMFLOAT4( .5f, -.25f, 0.f, 1.f ), XMFLOAT4( 0.f, 1.f, 0.f, 0.f ) }, + { XMFLOAT4( 1.f, -.25f, 0.f, 1.f ), XMFLOAT4( 1.f, 1.f, 0.f, 0.f ) }, + + // XNEG + { XMFLOAT4( -.5f, .25f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 1.f, 0.f ) }, + { XMFLOAT4( 0.f, .25f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 1.f, 0.f ) }, + { XMFLOAT4( -.5f, -.25f, 0.f, 1.f ), XMFLOAT4( 0.f, 1.f, 1.f, 0.f ) }, + { XMFLOAT4( 0.f, -.25f, 0.f, 1.f ), XMFLOAT4( 1.f, 1.f, 1.f, 0.f ) }, + + // YPOS + { XMFLOAT4( -.5f, .75f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 2.f, 0.f ) }, + { XMFLOAT4( 0.f, .75f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 2.f, 0.f ) }, + { XMFLOAT4( -.5f, .25f, 0.f, 1.f ), XMFLOAT4( 0.f, 1.f, 2.f, 0.f ) }, + { XMFLOAT4( 0.f, .25f, 0.f, 1.f ), XMFLOAT4( 1.f, 1.f, 2.f, 0.f ) }, + + // YNEG + { XMFLOAT4( -.5f, -.25f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 3.f, 0.f ) }, + { XMFLOAT4( 0.f, -.25f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 3.f, 0.f ) }, + { XMFLOAT4( -.5f, -.75f, 0.f, 1.f ), XMFLOAT4( 0.f, 1.f, 3.f, 0.f ) }, + { XMFLOAT4( 0.f, -.75f, 0.f, 1.f ), XMFLOAT4( 1.f, 1.f, 3.f, 0.f ) }, + + // ZPOS + { XMFLOAT4( 0.f, .25f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 4.f, 0.f ) }, + { XMFLOAT4( .5f, .25f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 4.f, 0.f ) }, + { XMFLOAT4( 0.f, -.25f, 0.f, 1.f ), XMFLOAT4( 0.f, 1.f, 4.f, 0.f ) }, + { XMFLOAT4( .5f, -.25f, 0.f, 1.f ), XMFLOAT4( 1.f, 1.f, 4.f, 0.f ) }, + + // ZNEG + { XMFLOAT4( -1.f, .25f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 5.f, 0.f ) }, + { XMFLOAT4( -.5f, .25f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 5.f, 0.f ) }, + { XMFLOAT4( -1.f, -.25f, 0.f, 1.f ), XMFLOAT4( 0.f, 1.f, 5.f, 0.f ) }, + { XMFLOAT4( -.5f, -.25f, 0.f, 1.f ), XMFLOAT4( 1.f, 1.f, 5.f, 0.f ) }, + }; + + static const SimpleVertex vertices[] = + { + { XMFLOAT4( -1.f, 1.f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 0.f, 0.f ) }, + { XMFLOAT4( 1.f, 1.f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 0.f, 0.f ) }, + { XMFLOAT4( -1.f, -1.f, 0.f, 1.f ), XMFLOAT4( 0.f, 1.f, 0.f, 0.f ) }, + { XMFLOAT4( 1.f, -1.f, 0.f, 1.f ), XMFLOAT4( 1.f, 1.f, 0.f, 0.f ) }, + }; + + static const SimpleVertex vertices1D[] = + { + { XMFLOAT4( -1.f, .05f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 0.f, 0.f ) }, + { XMFLOAT4( 1.f, .05f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 0.f, 0.f ) }, + { XMFLOAT4( -1.f, -.05f, 0.f, 1.f ), XMFLOAT4( 0.f, 0.f, 0.f, 0.f ) }, + { XMFLOAT4( 1.f, -.05f, 0.f, 1.f ), XMFLOAT4( 1.f, 0.f, 0.f, 0.f ) }, + }; + + if ( isCubeMap ) + { + nverts = _countof(verticesCube); + InitData.pSysMem = verticesCube; + } + else if ( is1D ) + { + nverts = _countof(vertices1D); + InitData.pSysMem = vertices1D; + } + else + { + nverts = _countof(vertices); + InitData.pSysMem = vertices; + } + + D3D11_BUFFER_DESC bd; + ZeroMemory( &bd, sizeof(bd) ); + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = sizeof( SimpleVertex ) * nverts; + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = 0; + hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer ); + if( FAILED( hr ) ) + return hr; + + // Set vertex buffer + UINT stride = sizeof( SimpleVertex ); + UINT offset = 0; + g_pImmediateContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset ); + + // Create index buffer + static const WORD indicesCube[] = + { + 0, 1, 2, + 2, 1, 3, + 4, 5, 6, + 6, 5, 7, + 8, 9, 10, + 10, 9, 11, + 12, 13, 14, + 14, 13, 15, + 16, 17, 18, + 18, 17, 19, + 20, 21, 22, + 22, 21, 23 + }; + + static const WORD indices[] = + { + 0, 1, 2, + 2, 1, 3 + }; + + if ( isCubeMap ) + { + g_iIndices = _countof(indicesCube); + InitData.pSysMem = indicesCube; + } + else + { + g_iIndices = _countof(indices); + InitData.pSysMem = indices; + } + + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = g_iIndices * sizeof(WORD); + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + bd.CPUAccessFlags = 0; + hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pIndexBuffer ); + if( FAILED( hr ) ) + return hr; + + // Set index buffer + g_pImmediateContext->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 ); + + // Set primitive topology + g_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); + + // Create the constant buffers + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = sizeof(CBArrayControl); + bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bd.CPUAccessFlags = 0; + hr = g_pd3dDevice->CreateBuffer( &bd, NULL, &g_pCBArrayControl ); + if( FAILED( hr ) ) + return hr; + + // Create the state objects + D3D11_SAMPLER_DESC sampDesc; + ZeroMemory( &sampDesc, sizeof(sampDesc) ); + sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sampDesc.MinLOD = 0; + sampDesc.MaxLOD = D3D11_FLOAT32_MAX; + hr = g_pd3dDevice->CreateSamplerState( &sampDesc, &g_pSamplerLinear ); + if( FAILED( hr ) ) + return hr; + + D3D11_BLEND_DESC dsc = + { + false, + false, + { + true, + D3D11_BLEND_SRC_ALPHA, + D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_OP_ADD, + D3D11_BLEND_ZERO, + D3D11_BLEND_ZERO, + D3D11_BLEND_OP_ADD, + D3D11_COLOR_WRITE_ENABLE_ALL + } + }; + hr = g_pd3dDevice->CreateBlendState(&dsc, &g_AlphaBlendState ); + if( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//-------------------------------------------------------------------------------------- +void Render() +{ + float ClearColor[4] = { 0.f, 1.f, 1.f, 1.0f }; //red,green,blue,alpha + g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor ); + g_pImmediateContext->ClearDepthStencilView( g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 ); + + float bf [4] = {1.0f, 1.0f, 1.0f, 1.0f}; + g_pImmediateContext->OMSetBlendState( g_AlphaBlendState, bf, 0xffffffff ); + + CBArrayControl cb; + cb.Index = (float)g_iCurrentIndex; + g_pImmediateContext->UpdateSubresource( g_pCBArrayControl, 0, NULL, &cb, 0, 0 ); + + g_pImmediateContext->VSSetShader( g_pVertexShader, NULL, 0 ); + g_pImmediateContext->PSSetShader( g_pPixelShader, NULL, 0 ); + g_pImmediateContext->PSSetConstantBuffers( 0, 1, &g_pCBArrayControl ); + g_pImmediateContext->PSSetShaderResources( 0, 1, &g_pSRV ); + g_pImmediateContext->PSSetSamplers( 0, 1, &g_pSamplerLinear ); + g_pImmediateContext->DrawIndexed( g_iIndices, 0, 0 ); + + g_pSwapChain->Present( 0, 0 ); +} + + +//-------------------------------------------------------------------------------------- +void CleanupDevice() +{ + if( g_pImmediateContext ) g_pImmediateContext->ClearState(); + + if( g_pSamplerLinear ) g_pSamplerLinear->Release(); + if( g_AlphaBlendState ) g_AlphaBlendState->Release(); + if( g_pSRV ) g_pSRV->Release(); + if( g_pVertexBuffer ) g_pVertexBuffer->Release(); + if( g_pIndexBuffer ) g_pIndexBuffer->Release(); + if( g_pCBArrayControl ) g_pCBArrayControl->Release(); + if( g_pVertexLayout ) g_pVertexLayout->Release(); + if( g_pVertexShader ) g_pVertexShader->Release(); + if( g_pPixelShader ) g_pPixelShader->Release(); + if( g_pDepthStencil ) g_pDepthStencil->Release(); + if( g_pDepthStencilView ) g_pDepthStencilView->Release(); + if( g_pRenderTargetView ) g_pRenderTargetView->Release(); + if( g_pSwapChain ) g_pSwapChain->Release(); + if( g_pImmediateContext ) g_pImmediateContext->Release(); + if( g_pd3dDevice ) g_pd3dDevice->Release(); +} + diff --git a/DDSView/ddsview.fx b/DDSView/ddsview.fx new file mode 100644 index 0000000..88ef8f3 --- /dev/null +++ b/DDSView/ddsview.fx @@ -0,0 +1,85 @@ +//-------------------------------------------------------------------------------------- +// File: ddsview.fx +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------- +// Constant Buffer Variables +//-------------------------------------------------------------------------------------- +Texture1D tx1D : register( t0 ); +Texture1DArray tx1DArray : register( t0 ); + +Texture2D tx2D : register( t0 ); +Texture2DArray tx2DArray : register( t0 ); + +Texture3D tx3D : register( t0 ); + +SamplerState samLinear : register( s0 ); + +cbuffer cbArrayControl : register( b0 ) +{ + float Index; +}; + +//-------------------------------------------------------------------------------------- +struct VS_INPUT +{ + float4 Pos : POSITION; + float4 Tex : TEXCOORD0; +}; + +struct PS_INPUT +{ + float4 Pos : SV_POSITION; + float4 Tex : TEXCOORD0; +}; + + +//-------------------------------------------------------------------------------------- +// Vertex Shader +//-------------------------------------------------------------------------------------- +PS_INPUT VS( VS_INPUT input ) +{ + PS_INPUT output = (PS_INPUT)0; + output.Pos = input.Pos; + output.Tex = input.Tex; + return output; +} + + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- +float4 PS_1D( PS_INPUT input) : SV_Target +{ + return tx1D.Sample( samLinear, input.Tex.x ); +} + +float4 PS_1DArray( PS_INPUT input) : SV_Target +{ + return tx1DArray.Sample( samLinear, float2(input.Tex.x, Index) ); +} + +float4 PS_2D( PS_INPUT input) : SV_Target +{ + return tx2D.Sample( samLinear, input.Tex.xy ); +} + +float4 PS_2DArray( PS_INPUT input) : SV_Target +{ + return tx2DArray.Sample( samLinear, float3(input.Tex.xy, Index) ); +} + +float4 PS_3D( PS_INPUT input) : SV_Target +{ + int Width, Height, Depth; + tx3D.GetDimensions( Width, Height, Depth); + + return tx3D.Sample( samLinear, float3(input.Tex.xy, Index / Depth) ); +} + +float4 PS_Cube( PS_INPUT input) : SV_Target +{ + return tx2DArray.Sample( samLinear, float3(input.Tex.xy, input.Tex.z + (6*Index)) ); +} diff --git a/DDSView/directx.ico b/DDSView/directx.ico new file mode 100644 index 0000000000000000000000000000000000000000..bc43c1b2085df668dffff26d80adcb0ef73f23a9 GIT binary patch literal 25214 zcmeI5cVLg#_xSHS39q)lv}=A(BYsjRYY` zH7TWNZ8b`*#-_&kz0UJaROjc@&v*UvdpUTaG(n_2(TGm{_PcFOh|~~Ux5GdBRM&l zyS@?YKO>^Qtc#yK@=u5{>Hq1KU{6RG^@qxdybhWe6!5z*iT1>iBLf2i0)G1;F)?^# zV8BFP|K>v)#c5rObN$+f-L4fz{=Pz5qICr&I2w9ZA=o!DF>ZWtf_-8F&HR|vwHA+S zP{nE~FzDAF;&|;H84$z(0_?9pt3dI%xLlFpBZEgyw2$=+&#I6b7nfMqGcq#Jo?s8G z>p4OzD5~12M`UDVKydKL!F9QFDK%(Ci${(KPDs!KRM7WnalYOj`aV1;F?cLL!o&HH zo}QYR=;fsCL!B+pF9>D>DqtX+7fufKQRxlhe0_a0Thhkeyn_+~CQ{y; z@%qw|zKo3Ya1I_BkPwiRKHirPap3Byuh&#)kYKk51iqc_cQV6y=gx#&M~CO@L-E!DDMq{D4+R$L?E-5kdTxfq^}{V z_R1Ab1$!d>rj4h4Uq|aorEn_P6QL)?y&bKD>Y%L(3D6A!c{^J1u&ALu1T$YCvv07zrvnFZ+6(= z!6P&>)SF>b!Oc5So1q~)?>!?cSoAO+&WTntdR!`nNv zb#2Msz{^zdR)W)mg`~cvLSh3i$ZcKETGtOj<9V&ceSNL5>O1SQ2iYm_>+2vv)?4oO zVC(vAY5ksR*Xy~GfAJGJWQn3*@5$vY`SjS>tb2wrQ&nCi8&h8I$gKQ&N0Pbn&r|Ns z+#hqdaeu&FKT?f~#+=o=|F8FCeJ+oD%)E!_J-;Oi|Mae(jjQ$Vf6F~YoBEUflJ)&_ zc%9Yf=kU%NPuBRd#+x;Mp}5lH8GVvzPM@>rmqkwr$#2osBDh1(xKXzm@PDpn(hw@6 zeq|K<#EB}>vtFboCR*Y;vjmT-B^+XuGRWN^>Z^9p8fl4fb;E-bf`WL~8fkG}p5Y@$ zP8^Fw*5^c(l4}y|bt4hD`dOu8Kw?lXmB6Erv$|-*gU0Jy>)DbHBfTQIkcZ>rkhY$Y zYgBZ2ObkdzXj%n$40YxiS;=bE!lBM8h{vOP=SUTm z$VuN!v4jLmNU+3_Cm%yCkv3Arik1$wunc}XJ(c0{{Dt-ziX=%H@1lQ$VViZO@cg#p!Lh#i_pB~&XL-tMg_ozop8jWHRfS|$MhI-21%{>l zcaT+1@5$6Xo7s-GscmZ?+L!jJ_EZ7D4fx;T^R!Gpw8Ns%AkH-?5WY#P8yP${JRm(K zS7cz_$bfV-*cw=Fl%=vlCyw%tgTtyMjIxwcVB#oCQHBR6r8qIyk?Yb^>qf5f3iVA- zAMcsls{#|3T9+A88mBf;%8SH#J@6kn@@)RdfMnP7^blunj}YfI3C0!~Zfxo4##YzU z*a8yuImCGs-<>>?UBiPYANjz4RQhl2&Kywd&#$ArJ_zgwz6R2OFM++l9$*)+6W9(U z0b2n(unC9*Vt^=MEf5ZT28;&+0Y4zrW-|+bQ%+7M8Q2C)=D8QJ-`Uxu06TyfAl}8r zyvO}g_Uxt--@oO)2MEiS&3xzTYPJB$IdYhGl&MQQ@%)YgR`NbIS1$7~Up})vcW(0o z_w+n@%%{9Nls~`OQ?Q_k;{HqV;^rqHjOUXjN|=2mOPZ(d?#9mVI~6LJ!{y4E3mzV3 zFlDb-s$@!m&l68ivxIVa=x=q|vgU4$8pb6Bbz|?*!+1lFd*jC& znKa2%D^kSB=Qvw{)q= z;8~V0Hxe3Z9<5wyRt+C+rh~tPhnut+GmNZXZ!-8T8#kIe>(&`bNHCA$;*4zBVjgbV zWMunxb9U`oBfEAP*|W#UzJ2C0-(>%OGoL;lfBm(&#C#3p2K5%ZzR2Oyfi!PV`}W;|-NU#+iPz-MwqF^E*3z zI6=pp{LTa9OingAKKQ_7A2!T5*QsL)+`erJf^RNpnw_~X#&4FWDR%X$$unh&ae;q| zef5eQ)Svvx1^={H2Xn73fbvX!gC!opTj<&PEGv}x0#1xnhlWzQG-zCsr>W`C#^ zRKVknl{zawuv?;$xzYUAT7V>6n+`q4B|!LsGZI zT)2KuE397mc~%E4{e8+iLX!rtVJO z92b3n4j#>Uv3s8Z18?r4fv9iK{Rj=#g|ArZVAgK_HA?3#S+4EXwA4L26F0?tt0M^P z^kVNpM-$^?Hf-1sv;XwvhfhPlSh~P!ziXo!rE?XmNQI-@_ogHz#2#Y=le=|oosqIB zHYR3cyghN}srwP3%N^yrb!}d~OwL^SOO~tL;lvJbvd5gea^vanff?H<9vesL)T1}& ze(>qCr3>cKftJscD~D_DB1MaLID2&ap5%nDFI>6(^UptQ+z=hJ(Vo2L%bOu{=gj|j z>5>n1g;Ksk-rPA|v$+{&? zzC5{HU2Mjdw`{ErX(?MaMjg`9-yZwn!LZ>229F#)cKoF2vp<|Y%~8H!!Tfo1(}B&H zg2gI-w|5Iv$6`8g?A)dM!w0<7|CJ#DBSy1;W=^4eVDH9-3Kh)n=)ky?EPi&cJ$6GB z7v+Dty?8*MzJ2@k?>}Vhw3#y|QU0};n^Y@Y5R7tWceWYllC_Q|!$wh2RAFXs=u~&_ z)vH(EfJrl^jkn5otW%f@7Di6_%AMG`DK zZ#n=YXq2;1@iVj@4G+X6Y_`Y8z?^p<4QR&Iu2;bLv07eU>)oVI39CZBl7sfxH%5a= zOx)IOyS61J#99?z)5t*g{v!gkxV$#BW0N9Up+vEaU2)OTiu=aIy+_khQ<67Z6@FQS z=xEpLl_8Gu+Ch;bMatGZnq)N|6%}jWd$YsUgJ161rWLMT|0SZOceCz&9pxoxXvYR6 z%a(ih=uVJPLPp1JyIH&@ykS*1cHzp6hr^q;>)y+1USz^+z1zb&2UB;!{fbTO)+05G z6e(KEQDNi0@6X@w(X>;qewpRzpm&dQmCx?jYgO0~vuV$uLN3_~Dl^f;R(taCnvMNB zWi?L+K||ZOuGJy4LQMR&nci z@o2GJE>2X)M}>piQ;&A8*wC+2pI?iMyy>X$ouZVuw|HTegNrj2iq^V%l;S>qZC-f3 zxX9FJ6;k%xESoPk3yvDu3Ky%@wsYCKO6G>DMsYHzl zLx=Y6-L*Ma*RI{T{-(6{GU3gsZ@v~782IwbFAuOTN7sM%?`yB?HD=710R#T7*gyN0 zWe4d!xjd$qk*qiiPUuv9PR@+Ogy5j+Gq%(Jk6Zm3x67i(e=TnH*M2JGH`{jGq>?G7vcl9rvA5; zE1Qe>KM55pnkV>~8TgrdvHKr1Zfw@$M+iRU5u(}Bw5j=tcY<$_fe-m5K3lT-lpQ)4 zd;R*RGyQyrPbyuzT7Ku(l<@@DAKSGvmFeduKH_$KiuKf6gb$;>=uXOSZQk6((zd+x zlKB!}W;;G@EboVd4#v^g&>tk&z=!96o9i{;m3+AJNvE z_`zZ7qfVS?meF?t{p=q+*a&{hWqhrUj8~>lH}~+JBJf{}ft$>oYnJutV*>H7pW=5$ zQvQ(ote<}R%m*<4j0G%?Z0wr%E?-EL;!7fVWt z+5XW-M)1YH9$m z78$c(fid{@X2=j@!zZ^*n`WGnl8iGxq4TwC#u-1>>9fy_%j3t!1z*+$-_MD;a9Og% zxZ=mU;wNXP?`-($E{xZi_&^SDcftS9NgMeXTXxzn@XIeI7wr_`Irr?@rYQBw;Cq(C zH?6=uC;U}|d#xKcj0b&q>o@#fKl=5I*K5gFq@=NpK#fxSNc^XDi&j0WaMR2?*FgAGFtoaK*Ui7iUWPetwbmM=L)6bWvsx4Xc$ddnO_B*u{+d@4Ynm zwYMf8i8_D#$%@ZEaUj{eo?DrtN%rXQAFj~Az)=%I?dy(Tp}=PgtYJ2(TcPlQok{i$ z;pea4f4A?D$=}38hW&8m)}!T%=W7p5Jc}1Bma+?zFydmy!&e7{#;;!+v3B3NYfqQW z<#U_Ho;mXtF1CBmj;(QrF5mcQ@>dZNYop>4zW!;+!nw2bdnK1#`3e_1us3;2%*l&C zo;$W`_1c)Nd%u`I^Mg4v`0nfB?2;=_!J>D*PmEuC{D;%WkEL%o96EWzTT^GL;or@# zqKlKWYw`Sri|yI8F6`LxBO(s|aOK{_E*(4f96JR=erS(IrQEV- z&t3LZ{JJ&aYu3kX+;He(#xEhgTelxPezXu~YgEP6)xFUE^=rachp&sV-W?3DFT$ z-~3LIVn>rVM@O7Jce7=yj!NHG#|?O)*3q35Si3g*Oo6aIX?by{f4bN>vtJ9 zF|bdu6lQ2+?BTLGa^x?1VAuAtwk?`u@|X`T65 zUUd}MmHd4^4l}ssDO~G9jVcXWcggxL0TdYd&VdsJb3;j|TzSgWuG6qZr)Qsq0u$eu zJhGX4sq!8rOO&f#$FKQsK2vgB$fOAa+W9qTP`7d8CT%)&c&@VJ=gZ^Xe0|K&LH)b- z?%n%OK5L==Lx&C-GNgCcKh(8eum|vezwhwZc1jYmN^KO4lWzpnKFNxaui^I!U?DIC zP#Z;K{oX=w;d`4_}BX+_8exF5$-$wu6MxSQ@b!m4o@$GbMh|$;>n;JGW zt!OjV-`_~DUS=n{S%@{ys$JWhL}%a8SS~t0347u??=}AXgt%e?`ahJi5xk#=KG=*s zv61pG(r5Pnci?U9S553~^z3>n-zm>Qp0h=9m!G{hxns>Fqnn`48x$ui7o> z>k`E1e?foAjveL+dRpVcmw7&luGxyNpNqaaMLhE$`nw@^gr$$sJytweb+YPXVxd+% z7@cL%B~~n0PXY!t&7bhpVy8#daop_6Rrf7`5C#)%lR)5(*@ z>6>qiGjU&M^p`7r=Dc;wj}{h1eCpC7%K9le{4F&3qsE8{PT4lGOgY}jn& zu^C*Mll<6170`bk)TxBMkeir(UHWc>j`XLkGGoS=meljbPQc+YJ&9cx!qyu_eET)} z91W0%frN@p|9x#tXh zJ7ngHdX3w4SX05(#T0yZ@SxY<|D;(Juhwg7=F3^;jX?v43=96~(|%Qb)~xZUHahT? z0dsqdnKXNGCHiYpYwU=CH;d<*HEKeQ%8F3S7E_0RZga`=VRLtVT(hQ4x3F?Wik9`P z=y>E`zXo+`S9d%iqZ>Z(cW2-7^6g~To+Cn>-(4Bv{Ah)-En-pW!fIH}*v_zzc`+lz z`K7@j&XbQMyWW0sHvcCJ9>DNbnIZMj1_3tX#=rkFSF>i#T#6Sjo}1PS0VM%9pc0@c zsEep~eRc!%yOzrZI0OGzmlGZ5%AGrR@$A{NS95W3Y3l6k+#ML?ZeO#mHNF+j(n?fs8l@PkchU8PEuyyeQ3^C(fGgnzZ-#Rp9+S8mCQYSp&y zXwcxh?_0IHbftUuJNE|;eDrkWNVz|D?4zH?jk|N?wb!m}89MaDqF%lB4Qkvtx^UjS zvl-J^5bgyu2Fd|ChyNpU5)u;P#Kuf+cX#*lWy+LkHl=v++41Gd?KxAm>J@3&P^2}s zRkv<}Zp1bs{HhV#NkoySP8Ie}1z%OhnSp>W zP#VYq*nt1?r8MU_O|~XYniM5=SATA~a_{dhRqDWG$YljXU%1@|PZNS#@<+opTcfb3na%Fi^tCmRp`XVh`3b6||8(tO} zjdA|gTMpbm_&{U{+ce9UJ8+JS6xpyr@clVX5%&kUv1ZM^mE*=8u2;G8a`mf!NE7P*>zEVQMxPs8#jvB?IKA@!alhJwrvyQ7sCFz^)2nAk1h>u-`>s`-b5y} z1^-fj3-E7UnfN<%?kH!ME?p|FuT*LANe_=}KZE-%PfxiE&)nz!sA^St%DX33s>p*n zb>x1dM)E}0K=Q?7pf`gz1utoZ2}clX=Z!1G4Mijv{sA@IH2D^*G! z7A-0d3Ko>Bg$l`0H#a%Syxf7GIG-V$!EmhOz(B!X70y5iXC6eBv9GASuk*kdo~ES8 zlP|xNr)gF|A=EoPxtQ^&RdA=-YwYL))?LyFhJ+w zP4L(CplkY115J&eaGfBS7~T|fEoL*a~xaF#?q9x*~b zWNs4StskNJBgOx~0TG=K9Ru&quU>sCutNu354xsRE~#w&&6P=emmNEHen`%P_mA1K$pvR;`3Ah-1w7#u&UXmsGo<5{D>9DXtA-4bAK;(ISu#o2 zGw+14vF9Q(ZJKa?Mh+mOrXsT%^zJSG@cUb=gDtG@pLXt)$5~@geuBS)a48D0&rXxdA;C;>}$#@$>s4^J#-QV1Z3uKB&MGTkfBi6ait0JlajK`EU7+B?rfsfu z)vCSnb-8jkkAVA8=>9#p9|!M~;G70v$Ve&0owY}NRXF=2#i)!H5HOY3fA~Qn;F%wh zr;n9~R3B%}0lI;+O|lQ&JjBOG3bt)4PK=kcJkpqUMsi)Xtr)IUq(a^1=&5LHf&MbTenRWEpF_c8L<=j4ZM-UTvom zbU!(|ibq;$2Z!)jTjvkeL%!A6M$0}1<7n~E5JK^CE zU-tKp&Rhp-w_2CVdwYcnE50gIM)pDbeaMwP(4Ao~R9LEc)&GupN-xy{}E z_}-EwWe>F9jjY)U-t&OwKpxh!4VoI{q=60w8E4L)7Xz-Gdz0++TN<$D0DhPV-PXc0 z7kqu?2|D3E<4;FF3`3U`LcZGIhv#XahE`sn6ziZibJ(6XhTXU!A0R_hR;`lj*c^`) zf9$6dbLU)}a?*e6R{0cNaSFO~wousr5m~W9hWPu(LjUgIuYR1_ zSgF#iUC?HGks`7k-eLbwwu5sNFazicAauo<@fmCagDuZKj2PyYGlXK$0V+!iGX`(Q z&;xu%Gq&Z(($pF?BmvsIjGa*!xnxrwWPI!sIo1$36Q6R7NBQ#jadE~b;#{CKzz&~> z{yvS(%^69Hf7Fip{B{9f$ST*h-eq zgZ=c~#EDn)=g&V0)HNnxw$!L`X)AM_z#MaKSrWisaZU#I0Q)lU#D6V4fD#uLzp!{5 za{-;Lb^ZPKmTb)f?iHAWTHI?uXE&fAzq9eqWPy$WyMtsuaZ$Xn0qY=}hCr9)%x^k# za20*WnO%nu!8#TN*5B3O;Jcji3CWZ}Cb3SP`*!HRu|NTdMefIe|7LL82J8a91XLF& z=zOpPDQ^IcfINT=o8RIkbd5z%WV*_e5(*H7e zW*>GDXO@LC%kntOhn_X-S05B#;=$W*KPp3Nsg_u#+oe zyLLck&%zhli;TU5-sW7kJXT*+?IyMT)F)McT77BtN0A**(I3P<-pQi>)`ku5$Kj)_ zN4~9NeX88ib*nNo33z@Eh*%4|#c~+f01N}l1J2l^20pd$SKS7VD%YJe!6kDHYHyU~ z8|R6o9eB+^wtkI{xQ)JkqPkjrX|=u8_UqHfVT0>DC?8>aYb@izm@$t_mMj^J9;>YM z-`uq6N&;V9pD_aynv1TK6b?cX#Y_C25dts9-y|L#x&G^QX5eHAdSVm|Gr2_h+KR1 z)w`ujmwr7*jvVfae?s%--^M|QweaC;*6$kle;qhI&qv8T?*-BU<)80>uYpB?@=!L_ z&#cAgZ6mc$tug3W_-zA}pUNu!Ko9iyczl$##EdR$97f{-%!L(`LGE&P1LGWjROdqd zX>iwZob28EC*o&;=m1o!G4U;1#>XSCBC&tMa^#TJ%mMpLvK~;GqrComKGL-u4|E6e zW82tNAFEzcnXa-$aaX+cx4u(-r7|D+$r*O>1b@z-%MfJh^c_278+`K%aRE_)7ewPT z&{VLs1$ku6f%?+u?>(J59j{QKLLX2^l(R3^y!q?|=59SYK8!W63i~Gvydr?-*Fu&& z+L`&yvW@UTY%0H14=H|%ui~ws&-z~N%nKJBHV<}<%KjpZp$=nEyLl92n9JJV4-Sur z4+!>_V6O-=MOfRMrE$!G#`Rd&ku6%JRI664HK<$mPi&JWUG0o93R`6rJhYNISP2j5 z7}N&UG3Z**^{jGfKH#Oc40eenFI6ugGcDbubXQQjK<#X`w^etk-=Ol9b(>{x6l08V zjIdf|3~kK8Hsl0>(JhR52>md{v0hC5oaJIyOpW>axFIl*&j#U|ion|A}U_%BPbdkY! zG1!vqxjE(oz9I+0vi-X=hT%LKfe*3J9lQH zEU{qIF~qcJu`HhW(^~j%C3f8B@X%-A`Z@3k5CXIW^0Q8D>I10m&*Bxuo5n2A-^%aE zA*aw#$>r}a1!&6^JZ#7kGjpb8E3jwyn=wFPL3+vn=41?WHV0q%AnWlL=Go#Sja6dL z?(p|dCyv~H`0(Kj>{t9ZG-=W?p<~C}QLL9R=74-9`G`4~0?s{v%E;E7ue~PZr~Jw@ zS#*CMcYO!X+t5#i=}+yY4uB_+i}q~DMS~nP*fSPC>AGNT7Xa@njJ*-{Ij1ANv6nwY z9-YRY{iRwp%a6F*u;If+&6_Xggl3H$J9hjbjuP9ZP4q^1V-2>*GUi|!w%kkfSqXXM zij83VSaJsXkvAiqzw=*{|3HzZnDkMsALf zVP1072~pUi?@-yj38uzoY} z-wroycy@k|9wQemT2%4!<;$wVHP-p3`0F^L{QdjJckOzf{5uI_46|uxF!N9sIg*bt zIHMaa|4V&HT@NbL!QBPk()GF;*a@iJs=TA)P~3GKSz}OMssm(aojDC2EZMQ6y>jQ4 z>Covb?iYv=U1Oi*RGm6E*0yRj51y}e=+GgRIqF9ICGJ`C5Z$WPg!pdVe%5(d#a;zD zjq(a(@S^>^IuGb}gYGr>riM70jo3gb^rXrw)p2TXsO_z`>+i>*e5Lk@&QndG6#B9* z{4y1~??vvNCH{8_zWJec?K|;)e#__1o$Gz_0f97S5e8FQO5D8A|7ic0K$n$5!XukPrEYOx(Ue8yb-9K&27i)AMI zF&N$WK7D_|Sk|b_M{aQDTgXM07+@o?3E09r&VyS#l7e1Ig~!vN_cze{2y^&N&6+op zYS&)>S*uoUHgDcsG9x2H*XlFe{|c{v`aY&rs}~r@#*N*&-HpQTUyHtug0EtbQ_;{b znt6#P-V=-7iDS(t09$}0 zQJ3vc_0&Jb`}gJ7`T2EXZG0Y&FR>NdVk=|Wg6+G7dD)8Jl*pL2Vb5+y?{nT=cJq5L z@4iB}rvoWIJ|`2syh5XD)#7N>@Pe#SrL}^tQ+53<+`0ctS^0b1{>gK!+TP8Yb;E9& zy{S!`O&3&A-2AamoMo9rQvnQMB&h zm8{Rt-Opw|XWcXOnQ>Y4z%YEQ&)k(xro2TW$328+=p<*k3;$^ay;D%>$($Ki?p1m1 z1~dcy8_sDd1zm_w=H*@l&=^N~z#|j%x4zSFn4#9$hAgV{_@7;RzQ@hYEr+|idvRhb zo_aQ^Sg~T=3KuTizfhq<0R;;d9GNd)zL9zJ<{iqJtA6Z3bz%?1m)i}KgI(Z z+UPrb%a$#R`;o^zs$RXJYZ^BWO>NU=-`U>1&)p0Ry!CkE#K(`PPJNs)efsSaAt4u% z#*g1WuV25ICZ3)%vSrKm3V5`o?`l9nAd55k`PWxwzfSlQxqJ5PS>?lq4ae-MRcq_D zMvbnD#sS$sA~(ko_tSGr#P#``4%V3;>nsZXA4t<<>?s)Y;$Yx8K1(6pR#wOduY$j zz_9-!#BDBwgt5)JI^w~Z1 z@B{SRL+rPU=)ObX@iY3uItzkrZJo)%_qWbM=~*25wf5(Ae^SpHfQ$XT_YU|}tHz%# zaqgChgVNzQm-fA7%a+{izm%n`? z+1L{B;z_-(YK5F=^)DDf~z-m@HvGnfxmucS#!Ie@|f_JdJ&x``W+a z!CWXmWb&l03rmXY*duG#Zj)Z7%;~QgH@SVn!jatg;Wj-FLEOZZzCA%y7Q(~`Ic#Fsr`0_;YhRc>7|z>EE!yoy`$Jh@ zF&dL#U66Apv}6N)5Ot{k=-n>z_x z{0sUYM&B@put%n3#&_7S*S%@-5u6mK>U_Jx(s^icplAc`y+xKYDijZAV1t$4Z%xR!;c zUGNJQA}bC;$J^lYlz58j3)NYwJL%tp^Sux4_-f?KT74(d-)2B#%rk)gz%yDv$7g3O z7;|p=)V(+DzYtIy`jbm2o$yN*;@hXOM#(dk2hjeh?lb6q8#?Q8NXX-7{Wop;O9DPj zH2ufYSJuAB4t^^=RszZsC=YQ$2U&3$<#COv(kFS9k^|6ve)1lr5i(;kaW?X2g}>P( zPgPE;tX6%&9O!=8QhANMu=a~MBX@K7@LOubDF1C~-TDAI zzY>X`s{3?VIKZ=%4@`C_3VhQ4PBqzp#yoE5`m2P z20CPDoLl#FMhq?L(SI*s6!R2}9*m;>4C1BM-XF4>++>agm0@TmPi+1A z!^uIG4fxw%pl4?y1C-w6Ig2y<`qnzuzLeI=-wH}|#%<%=f)iu2;&$je3m5cGcJL$j zRa#TF8#EcjIgLHEFRHuQ6C-A`ioMcm{DGcq{0*@3_V(@1#^9fl$1UV`Ng?>hN#npO ze|7&*b%4s3tg*A^b#JaD`bzoIk`wT~6?fA;J1q~6g;}Q_%!@BJ(q`-#-FLh0=XZ6C zk57O*HZ9|~ba0HH-^UtD`Uo0zLFX2P53I9AdM-zCP(7$^s%%mD#_wEn=15=q*0rJQ z(~=SJ7Jur{A+MERkQJ`%-;&2J$I%gwp;c2s$EV0*0ru?O>g3Hr&bXg9nD&( z4(+ou50(!2_+tkSstZq_7ILITLJBJ`4C>?Z-7a4 z=)Qpdb^$7{R8IK-&-51kx?q?2Q9qJ)E}-9T5m(&as8N{O)oR;6-?#oo=Gvm0HvM7~ zI$d!HV7@$wt>%OuY-yM+{EY*dijAalY9nB$uSD*8HjBT*Ad$#W{)U3s(LINt-w%vCwN9P5&pLH#rnbHM z7k@SO=kd_FP=1K*-1&4Qd!vhqBh5$lFJlfP;f)yDkH=oKLz^V-JBjn{;oX<$ku+jp zhw&4>@$&j%yO-BIwX1h}k{H>h7jZzu8`=&Ki65>SNWORGT)lUl;oR%{qI&UHvT~ zHVqw2eL$GzED?uf?D7x?D7}MA7T?z38i^zu+a$x05 zM8r=oHETAH`jxWUP#%bF)TrBuk|i_98y0e6h1^gfC)Ya9Lo7kauNCq_g`8QrwRP*= zzW)B&hQ?J)O8@>PlZzBNMGmr%t19HI$^aTAZ&b+t6>@il98)1zRmd+Da&P5)WaO19 zrAw=w;SyKA{9EKP3%RM*UO)MMN&<8rMP(iNxz@Q0 za(pct)bi1gljKAT`Ont8=-hHHuuW8j3YXSUN9{XpL-joQzt7GFkxy*-OyuthInqKd zwskHeW6YS_%3~XRd{W5)mxa_Nr(4K5cGy8`kEnfu%}2hmkQ*%I{tEfeLN2$Q?9$~7 z=PJv`HElYcoNpnw+o8);pQ}y79FSuyr = (float) ((w565 >> 11) & 31) * (1.0f / 31.0f); + pColor->g = (float) ((w565 >> 5) & 63) * (1.0f / 63.0f); + pColor->b = (float) ((w565 >> 0) & 31) * (1.0f / 31.0f); + pColor->a = 1.0f; +} + +inline static uint16_t Encode565(_In_ const HDRColorA *pColor) +{ + HDRColorA Color; + + Color.r = (pColor->r < 0.0f) ? 0.0f : (pColor->r > 1.0f) ? 1.0f : pColor->r; + Color.g = (pColor->g < 0.0f) ? 0.0f : (pColor->g > 1.0f) ? 1.0f : pColor->g; + Color.b = (pColor->b < 0.0f) ? 0.0f : (pColor->b > 1.0f) ? 1.0f : pColor->b; + + uint16_t w; + + w = (uint16_t) ((static_cast(Color.r * 31.0f + 0.5f) << 11) | + (static_cast(Color.g * 63.0f + 0.5f) << 5) | + (static_cast(Color.b * 31.0f + 0.5f) << 0)); + + return w; +} + + +//------------------------------------------------------------------------------------- +static void OptimizeRGB(_Out_ HDRColorA *pX, _Out_ HDRColorA *pY, + _In_count_c_(NUM_PIXELS_PER_BLOCK) const HDRColorA *pPoints, _In_ size_t cSteps, _In_ DWORD flags) +{ + static const float fEpsilon = (0.25f / 64.0f) * (0.25f / 64.0f); + static const float pC3[] = { 2.0f/2.0f, 1.0f/2.0f, 0.0f/2.0f }; + static const float pD3[] = { 0.0f/2.0f, 1.0f/2.0f, 2.0f/2.0f }; + static const float pC4[] = { 3.0f/3.0f, 2.0f/3.0f, 1.0f/3.0f, 0.0f/3.0f }; + static const float pD4[] = { 0.0f/3.0f, 1.0f/3.0f, 2.0f/3.0f, 3.0f/3.0f }; + + const float *pC = (3 == cSteps) ? pC3 : pC4; + const float *pD = (3 == cSteps) ? pD3 : pD4; + + // Find Min and Max points, as starting point + HDRColorA X = (flags & BC_FLAGS_UNIFORM) ? HDRColorA(1.f, 1.f, 1.f, 1.f) : g_Luminance; + HDRColorA Y = HDRColorA(0.0f, 0.0f, 0.0f, 1.0f); + + for(size_t iPoint = 0; iPoint < NUM_PIXELS_PER_BLOCK; iPoint++) + { +#ifdef COLOR_WEIGHTS + if(pPoints[iPoint].a > 0.0f) +#endif // COLOR_WEIGHTS + { + if(pPoints[iPoint].r < X.r) + X.r = pPoints[iPoint].r; + + if(pPoints[iPoint].g < X.g) + X.g = pPoints[iPoint].g; + + if(pPoints[iPoint].b < X.b) + X.b = pPoints[iPoint].b; + + if(pPoints[iPoint].r > Y.r) + Y.r = pPoints[iPoint].r; + + if(pPoints[iPoint].g > Y.g) + Y.g = pPoints[iPoint].g; + + if(pPoints[iPoint].b > Y.b) + Y.b = pPoints[iPoint].b; + } + } + + // Diagonal axis + HDRColorA AB; + + AB.r = Y.r - X.r; + AB.g = Y.g - X.g; + AB.b = Y.b - X.b; + + float fAB = AB.r * AB.r + AB.g * AB.g + AB.b * AB.b; + + // Single color block.. no need to root-find + if(fAB < FLT_MIN) + { + pX->r = X.r; pX->g = X.g; pX->b = X.b; + pY->r = Y.r; pY->g = Y.g; pY->b = Y.b; + return; + } + + // Try all four axis directions, to determine which diagonal best fits data + float fABInv = 1.0f / fAB; + + HDRColorA Dir; + Dir.r = AB.r * fABInv; + Dir.g = AB.g * fABInv; + Dir.b = AB.b * fABInv; + + HDRColorA Mid; + Mid.r = (X.r + Y.r) * 0.5f; + Mid.g = (X.g + Y.g) * 0.5f; + Mid.b = (X.b + Y.b) * 0.5f; + + float fDir[4]; + fDir[0] = fDir[1] = fDir[2] = fDir[3] = 0.0f; + + + for(size_t iPoint = 0; iPoint < NUM_PIXELS_PER_BLOCK; iPoint++) + { + HDRColorA Pt; + Pt.r = (pPoints[iPoint].r - Mid.r) * Dir.r; + Pt.g = (pPoints[iPoint].g - Mid.g) * Dir.g; + Pt.b = (pPoints[iPoint].b - Mid.b) * Dir.b; + + float f; + +#ifdef COLOR_WEIGHTS + f = Pt.r + Pt.g + Pt.b; + fDir[0] += pPoints[iPoint].a * f * f; + + f = Pt.r + Pt.g - Pt.b; + fDir[1] += pPoints[iPoint].a * f * f; + + f = Pt.r - Pt.g + Pt.b; + fDir[2] += pPoints[iPoint].a * f * f; + + f = Pt.r - Pt.g - Pt.b; + fDir[3] += pPoints[iPoint].a * f * f; +#else + f = Pt.r + Pt.g + Pt.b; + fDir[0] += f * f; + + f = Pt.r + Pt.g - Pt.b; + fDir[1] += f * f; + + f = Pt.r - Pt.g + Pt.b; + fDir[2] += f * f; + + f = Pt.r - Pt.g - Pt.b; + fDir[3] += f * f; +#endif // COLOR_WEIGHTS + } + + float fDirMax = fDir[0]; + size_t iDirMax = 0; + + for(size_t iDir = 1; iDir < 4; iDir++) + { + if(fDir[iDir] > fDirMax) + { + fDirMax = fDir[iDir]; + iDirMax = iDir; + } + } + + if(iDirMax & 2) + { + float f = X.g; X.g = Y.g; Y.g = f; + } + + if(iDirMax & 1) + { + float f = X.b; X.b = Y.b; Y.b = f; + } + + + // Two color block.. no need to root-find + if(fAB < 1.0f / 4096.0f) + { + pX->r = X.r; pX->g = X.g; pX->b = X.b; + pY->r = Y.r; pY->g = Y.g; pY->b = Y.b; + return; + } + + + // Use Newton's Method to find local minima of sum-of-squares error. + float fSteps = (float) (cSteps - 1); + + for(size_t iIteration = 0; iIteration < 8; iIteration++) + { + // Calculate new steps + HDRColorA pSteps[4]; + + for(size_t iStep = 0; iStep < cSteps; iStep++) + { + pSteps[iStep].r = X.r * pC[iStep] + Y.r * pD[iStep]; + pSteps[iStep].g = X.g * pC[iStep] + Y.g * pD[iStep]; + pSteps[iStep].b = X.b * pC[iStep] + Y.b * pD[iStep]; + } + + + // Calculate color direction + Dir.r = Y.r - X.r; + Dir.g = Y.g - X.g; + Dir.b = Y.b - X.b; + + float fLen = (Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b); + + if(fLen < (1.0f / 4096.0f)) + break; + + float fScale = fSteps / fLen; + + Dir.r *= fScale; + Dir.g *= fScale; + Dir.b *= fScale; + + + // Evaluate function, and derivatives + float d2X, d2Y; + HDRColorA dX, dY; + d2X = d2Y = dX.r = dX.g = dX.b = dY.r = dY.g = dY.b = 0.0f; + + for(size_t iPoint = 0; iPoint < NUM_PIXELS_PER_BLOCK; iPoint++) + { + float fDot = (pPoints[iPoint].r - X.r) * Dir.r + + (pPoints[iPoint].g - X.g) * Dir.g + + (pPoints[iPoint].b - X.b) * Dir.b; + + + size_t iStep; + if(fDot <= 0.0f) + iStep = 0; + if(fDot >= fSteps) + iStep = cSteps - 1; + else + iStep = static_cast(fDot + 0.5f); + + + HDRColorA Diff; + Diff.r = pSteps[iStep].r - pPoints[iPoint].r; + Diff.g = pSteps[iStep].g - pPoints[iPoint].g; + Diff.b = pSteps[iStep].b - pPoints[iPoint].b; + +#ifdef COLOR_WEIGHTS + float fC = pC[iStep] * pPoints[iPoint].a * (1.0f / 8.0f); + float fD = pD[iStep] * pPoints[iPoint].a * (1.0f / 8.0f); +#else + float fC = pC[iStep] * (1.0f / 8.0f); + float fD = pD[iStep] * (1.0f / 8.0f); +#endif // COLOR_WEIGHTS + + d2X += fC * pC[iStep]; + dX.r += fC * Diff.r; + dX.g += fC * Diff.g; + dX.b += fC * Diff.b; + + d2Y += fD * pD[iStep]; + dY.r += fD * Diff.r; + dY.g += fD * Diff.g; + dY.b += fD * Diff.b; + } + + + // Move endpoints + if(d2X > 0.0f) + { + float f = -1.0f / d2X; + + X.r += dX.r * f; + X.g += dX.g * f; + X.b += dX.b * f; + } + + if(d2Y > 0.0f) + { + float f = -1.0f / d2Y; + + Y.r += dY.r * f; + Y.g += dY.g * f; + Y.b += dY.b * f; + } + + if((dX.r * dX.r < fEpsilon) && (dX.g * dX.g < fEpsilon) && (dX.b * dX.b < fEpsilon) && + (dY.r * dY.r < fEpsilon) && (dY.g * dY.g < fEpsilon) && (dY.b * dY.b < fEpsilon)) + { + break; + } + } + + pX->r = X.r; pX->g = X.g; pX->b = X.b; + pY->r = Y.r; pY->g = Y.g; pY->b = Y.b; +} + + +//------------------------------------------------------------------------------------- +inline static void DecodeBC1( _Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_ const D3DX_BC1 *pBC ) +{ + assert( pColor && pBC ); + static_assert( sizeof(D3DX_BC1) == 8, "D3DX_BC1 should be 8 bytes" ); + + static XMVECTORF32 s_Scale = { 1.f/31.f, 1.f/63.f, 1.f/31.f, 1.f }; + + XMVECTOR clr0 = XMLoadU565( reinterpret_cast(&pBC->rgb[0]) ); + XMVECTOR clr1 = XMLoadU565( reinterpret_cast(&pBC->rgb[1]) ); + + clr0 = XMVectorMultiply( clr0, s_Scale ); + clr1 = XMVectorMultiply( clr1, s_Scale ); + + clr0 = XMVectorSwizzle( clr0, 2, 1, 0, 3 ); + clr1 = XMVectorSwizzle( clr1, 2, 1, 0, 3 ); + + clr0 = XMVectorSelect( g_XMIdentityR3, clr0, g_XMSelect1110 ); + clr1 = XMVectorSelect( g_XMIdentityR3, clr1, g_XMSelect1110 ); + + XMVECTOR clr2, clr3; + if(pBC->rgb[0] <= pBC->rgb[1]) + { + clr2 = XMVectorLerp( clr0, clr1, 0.5f ); + clr3 = XMVectorZero(); // Alpha of 0 + } + else + { + clr2 = XMVectorLerp( clr0, clr1, 1.f/3.f ); + clr3 = XMVectorLerp( clr0, clr1, 2.f/3.f ); + } + + uint32_t dw = pBC->bitmap; + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i, dw >>= 2) + { + switch(dw & 3) + { + case 0: pColor[i] = clr0; break; + case 1: pColor[i] = clr1; break; + case 2: pColor[i] = clr2; break; + + case 3: + default: pColor[i] = clr3; break; + } + } +} + + +//------------------------------------------------------------------------------------- +#pragma warning(disable: 4616 6001 6201) + +static void EncodeBC1(_Out_ D3DX_BC1 *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const HDRColorA *pColor, + _In_ bool bColorKey, _In_ float alphaRef, _In_ DWORD flags) +{ + assert( pBC && pColor ); + static_assert( sizeof(D3DX_BC1) == 8, "D3DX_BC1 should be 8 bytes" ); + + // Determine if we need to colorkey this block + size_t uSteps; + + if (bColorKey) + { + size_t uColorKey = 0; + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + if(pColor[i].a < alphaRef) + uColorKey++; + } + + if(NUM_PIXELS_PER_BLOCK == uColorKey) + { + pBC->rgb[0] = 0x0000; + pBC->rgb[1] = 0xffff; + pBC->bitmap = 0xffffffff; + return; + } + + uSteps = (uColorKey > 0) ? 3 : 4; + } + else + { + uSteps = 4; + } + + // Quantize block to R56B5, using Floyd Stienberg error diffusion. This + // increases the chance that colors will map directly to the quantized + // axis endpoints. + HDRColorA Color[NUM_PIXELS_PER_BLOCK]; + HDRColorA Error[NUM_PIXELS_PER_BLOCK]; + + if (flags & BC_FLAGS_DITHER_RGB) + memset(Error, 0x00, NUM_PIXELS_PER_BLOCK * sizeof(HDRColorA)); + + size_t i; + for(i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + HDRColorA Clr; + Clr.r = pColor[i].r; + Clr.g = pColor[i].g; + Clr.b = pColor[i].b; + + if (flags & BC_FLAGS_DITHER_RGB) + { + Clr.r += Error[i].r; + Clr.g += Error[i].g; + Clr.b += Error[i].b; + } + + Color[i].r = (float) static_cast(Clr.r * 31.0f + 0.5f) * (1.0f / 31.0f); + Color[i].g = (float) static_cast(Clr.g * 63.0f + 0.5f) * (1.0f / 63.0f); + Color[i].b = (float) static_cast(Clr.b * 31.0f + 0.5f) * (1.0f / 31.0f); + +#ifdef COLOR_WEIGHTS + Color[i].a = pColor[i].a; +#else + Color[i].a = 1.0f; +#endif // COLOR_WEIGHTS + + if (flags & BC_FLAGS_DITHER_RGB) + { + HDRColorA Diff; + Diff.r = Color[i].a * (Clr.r - Color[i].r); + Diff.g = Color[i].a * (Clr.g - Color[i].g); + Diff.b = Color[i].a * (Clr.b - Color[i].b); + + if(3 != (i & 3)) + { + assert( i < 15 ); + __analysis_assume( i < 15 ); + Error[i + 1].r += Diff.r * (7.0f / 16.0f); + Error[i + 1].g += Diff.g * (7.0f / 16.0f); + Error[i + 1].b += Diff.b * (7.0f / 16.0f); + } + + if(i < 12) + { + if(i & 3) + { + Error[i + 3].r += Diff.r * (3.0f / 16.0f); + Error[i + 3].g += Diff.g * (3.0f / 16.0f); + Error[i + 3].b += Diff.b * (3.0f / 16.0f); + } + + Error[i + 4].r += Diff.r * (5.0f / 16.0f); + Error[i + 4].g += Diff.g * (5.0f / 16.0f); + Error[i + 4].b += Diff.b * (5.0f / 16.0f); + + if(3 != (i & 3)) + { + assert( i < 11 ); + __analysis_assume(i < 11 ); + Error[i + 5].r += Diff.r * (1.0f / 16.0f); + Error[i + 5].g += Diff.g * (1.0f / 16.0f); + Error[i + 5].b += Diff.b * (1.0f / 16.0f); + } + } + } + + if ( !( flags & BC_FLAGS_UNIFORM ) ) + { + Color[i].r *= g_Luminance.r; + Color[i].g *= g_Luminance.g; + Color[i].b *= g_Luminance.b; + } + } + + // Perform 6D root finding function to find two endpoints of color axis. + // Then quantize and sort the endpoints depending on mode. + HDRColorA ColorA, ColorB, ColorC, ColorD; + + OptimizeRGB(&ColorA, &ColorB, Color, uSteps, flags); + + if ( flags & BC_FLAGS_UNIFORM ) + { + ColorC = ColorA; + ColorD = ColorB; + } + else + { + ColorC.r = ColorA.r * g_LuminanceInv.r; + ColorC.g = ColorA.g * g_LuminanceInv.g; + ColorC.b = ColorA.b * g_LuminanceInv.b; + + ColorD.r = ColorB.r * g_LuminanceInv.r; + ColorD.g = ColorB.g * g_LuminanceInv.g; + ColorD.b = ColorB.b * g_LuminanceInv.b; + } + + uint16_t wColorA = Encode565(&ColorC); + uint16_t wColorB = Encode565(&ColorD); + + if((uSteps == 4) && (wColorA == wColorB)) + { + pBC->rgb[0] = wColorA; + pBC->rgb[1] = wColorB; + pBC->bitmap = 0x00000000; + return; + } + + Decode565(&ColorC, wColorA); + Decode565(&ColorD, wColorB); + + if ( flags & BC_FLAGS_UNIFORM ) + { + ColorA = ColorC; + ColorB = ColorD; + } + else + { + ColorA.r = ColorC.r * g_Luminance.r; + ColorA.g = ColorC.g * g_Luminance.g; + ColorA.b = ColorC.b * g_Luminance.b; + + ColorB.r = ColorD.r * g_Luminance.r; + ColorB.g = ColorD.g * g_Luminance.g; + ColorB.b = ColorD.b * g_Luminance.b; + } + + // Calculate color steps + HDRColorA Step[4]; + + if((3 == uSteps) == (wColorA <= wColorB)) + { + pBC->rgb[0] = wColorA; + pBC->rgb[1] = wColorB; + + Step[0] = ColorA; + Step[1] = ColorB; + } + else + { + pBC->rgb[0] = wColorB; + pBC->rgb[1] = wColorA; + + Step[0] = ColorB; + Step[1] = ColorA; + } + + static const size_t pSteps3[] = { 0, 2, 1 }; + static const size_t pSteps4[] = { 0, 2, 3, 1 }; + const size_t *pSteps; + + if(3 == uSteps) + { + pSteps = pSteps3; + + HDRColorALerp(&Step[2], &Step[0], &Step[1], 0.5f); + } + else + { + pSteps = pSteps4; + + HDRColorALerp(&Step[2], &Step[0], &Step[1], 1.0f / 3.0f); + HDRColorALerp(&Step[3], &Step[0], &Step[1], 2.0f / 3.0f); + } + + // Calculate color direction + HDRColorA Dir; + + Dir.r = Step[1].r - Step[0].r; + Dir.g = Step[1].g - Step[0].g; + Dir.b = Step[1].b - Step[0].b; + + float fSteps = (float) (uSteps - 1); + float fScale = (wColorA != wColorB) ? (fSteps / (Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b)) : 0.0f; + + Dir.r *= fScale; + Dir.g *= fScale; + Dir.b *= fScale; + + // Encode colors + uint32_t dw = 0; + if (flags & BC_FLAGS_DITHER_RGB) + memset(Error, 0x00, NUM_PIXELS_PER_BLOCK * sizeof(HDRColorA)); + + for(i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + if((3 == uSteps) && (pColor[i].a < alphaRef)) + { + dw = (3 << 30) | (dw >> 2); + } + else + { + HDRColorA Clr; + if ( flags & BC_FLAGS_UNIFORM ) + { + Clr.r = pColor[i].r; + Clr.g = pColor[i].g; + Clr.b = pColor[i].b; + } + else + { + Clr.r = pColor[i].r * g_Luminance.r; + Clr.g = pColor[i].g * g_Luminance.g; + Clr.b = pColor[i].b * g_Luminance.b; + } + + if (flags & BC_FLAGS_DITHER_RGB) + { + Clr.r += Error[i].r; + Clr.g += Error[i].g; + Clr.b += Error[i].b; + } + + float fDot = (Clr.r - Step[0].r) * Dir.r + (Clr.g - Step[0].g) * Dir.g + (Clr.b - Step[0].b) * Dir.b; + uint32_t iStep; + + if(fDot <= 0.0f) + iStep = 0; + else if(fDot >= fSteps) + iStep = 1; + else + iStep = static_cast( pSteps[static_cast(fDot + 0.5f)] ); + + dw = (iStep << 30) | (dw >> 2); + + if (flags & BC_FLAGS_DITHER_RGB) + { + HDRColorA Diff; + Diff.r = Color[i].a * (Clr.r - Step[iStep].r); + Diff.g = Color[i].a * (Clr.g - Step[iStep].g); + Diff.b = Color[i].a * (Clr.b - Step[iStep].b); + + if(3 != (i & 3)) + { + Error[i + 1].r += Diff.r * (7.0f / 16.0f); + Error[i + 1].g += Diff.g * (7.0f / 16.0f); + Error[i + 1].b += Diff.b * (7.0f / 16.0f); + } + + if(i < 12) + { + if(i & 3) + { + Error[i + 3].r += Diff.r * (3.0f / 16.0f); + Error[i + 3].g += Diff.g * (3.0f / 16.0f); + Error[i + 3].b += Diff.b * (3.0f / 16.0f); + } + + Error[i + 4].r += Diff.r * (5.0f / 16.0f); + Error[i + 4].g += Diff.g * (5.0f / 16.0f); + Error[i + 4].b += Diff.b * (5.0f / 16.0f); + + if(3 != (i & 3)) + { + Error[i + 5].r += Diff.r * (1.0f / 16.0f); + Error[i + 5].g += Diff.g * (1.0f / 16.0f); + Error[i + 5].b += Diff.b * (1.0f / 16.0f); + } + } + } + } + } + + pBC->bitmap = dw; +} + +//------------------------------------------------------------------------------------- +#ifdef COLOR_WEIGHTS +static void EncodeSolidBC1(_Out_ D3DX_BC1 *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const HDRColorA *pColor) +{ +#ifdef COLOR_AVG_0WEIGHTS + // Compute avg color + HDRColorA Color; + Color.r = pColor[0].r; + Color.g = pColor[0].g; + Color.b = pColor[0].b; + + for(size_t i = 1; i < NUM_PIXELS_PER_BLOCK; ++i) + { + Color.r += pColor[i].r; + Color.g += pColor[i].g; + Color.b += pColor[i].b; + } + + Color.r *= 1.0f / 16.0f; + Color.g *= 1.0f / 16.0f; + Color.b *= 1.0f / 16.0f; + + uint16_t wColor = Encode565(&Color); +#else + uint16_t wColor = 0x0000; +#endif // COLOR_AVG_0WEIGHTS + + // Encode solid block + pBC->rgb[0] = wColor; + pBC->rgb[1] = wColor; + pBC->bitmap = 0x00000000; +} +#endif // COLOR_WEIGHTS + + +//===================================================================================== +// Entry points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// BC1 Compression +//------------------------------------------------------------------------------------- +void D3DXDecodeBC1(XMVECTOR *pColor, const uint8_t *pBC) +{ + const D3DX_BC1 *pBC1 = reinterpret_cast(pBC); + DecodeBC1( pColor, pBC1 ); +} + +void D3DXEncodeBC1(uint8_t *pBC, const XMVECTOR *pColor, float alphaRef, DWORD flags) +{ + assert( pBC && pColor ); + + HDRColorA Color[NUM_PIXELS_PER_BLOCK]; + + if (flags & BC_FLAGS_DITHER_A) + { + float fError[NUM_PIXELS_PER_BLOCK]; + memset(fError, 0x00, NUM_PIXELS_PER_BLOCK * sizeof(float)); + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + HDRColorA clr; + XMStoreFloat4( reinterpret_cast( &clr ), pColor[i] ); + + float fAlph = clr.a + fError[i]; + + Color[i].r = clr.r; + Color[i].g = clr.g; + Color[i].b = clr.b; + Color[i].a = (float) static_cast(clr.a + fError[i] + 0.5f); + + float fDiff = fAlph - Color[i].a; + + if(3 != (i & 3)) + { + assert( i < 15 ); + __analysis_assume( i < 15 ); + fError[i + 1] += fDiff * (7.0f / 16.0f); + } + + if(i < 12) + { + if(i & 3) + fError[i + 3] += fDiff * (3.0f / 16.0f); + + fError[i + 4] += fDiff * (5.0f / 16.0f); + + if(3 != (i & 3)) + { + assert( i < 11 ); + __analysis_assume( i < 11 ); + fError[i + 5] += fDiff * (1.0f / 16.0f); + } + } + } + } + else + { + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + XMStoreFloat4( reinterpret_cast( &Color[i] ), pColor[i] ); + } + } + + D3DX_BC1 *pBC1 = reinterpret_cast(pBC); + EncodeBC1(pBC1, Color, true, alphaRef, flags); +} + + +//------------------------------------------------------------------------------------- +// BC2 Compression +//------------------------------------------------------------------------------------- +void D3DXDecodeBC2(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(D3DX_BC2) == 16, "D3DX_BC2 should be 16 bytes" ); + + const D3DX_BC2 *pBC2 = reinterpret_cast(pBC); + + // RGB part + DecodeBC1(pColor, &pBC2->bc1); + + // 4-bit alpha part + DWORD dw = pBC2->bitmap[0]; + + for(size_t i = 0; i < 8; ++i, dw >>= 4) + pColor[i] = XMVectorSetW( pColor[i], (float) (dw & 0xf) * (1.0f / 15.0f) ); + + dw = pBC2->bitmap[1]; + + for(size_t i = 8; i < NUM_PIXELS_PER_BLOCK; ++i, dw >>= 4) + pColor[i] = XMVectorSetW( pColor[i], (float) (dw & 0xf) * (1.0f / 15.0f) ); +} + +void D3DXEncodeBC2(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags) +{ + assert( pBC && pColor ); + static_assert( sizeof(D3DX_BC2) == 16, "D3DX_BC2 should be 16 bytes" ); + + HDRColorA Color[NUM_PIXELS_PER_BLOCK]; + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + XMStoreFloat4( reinterpret_cast( &Color[i] ), pColor[i] ); + } + + D3DX_BC2 *pBC2 = reinterpret_cast(pBC); + + // 4-bit alpha part. Dithered using Floyd Stienberg error diffusion. + pBC2->bitmap[0] = 0; + pBC2->bitmap[1] = 0; + + float fError[NUM_PIXELS_PER_BLOCK]; + if (flags & BC_FLAGS_DITHER_A) + memset(fError, 0x00, NUM_PIXELS_PER_BLOCK * sizeof(float)); + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + float fAlph = Color[i].a; + if (flags & BC_FLAGS_DITHER_A) + fAlph += fError[i]; + + uint32_t u = (uint32_t) static_cast(fAlph * 15.0f + 0.5f); + + pBC2->bitmap[i >> 3] >>= 4; + pBC2->bitmap[i >> 3] |= (u << 28); + + if (flags & BC_FLAGS_DITHER_A) + { + float fDiff = fAlph - (float) u * (1.0f / 15.0f); + + if(3 != (i & 3)) + { + assert( i < 15 ); + __analysis_assume( i < 15 ); + fError[i + 1] += fDiff * (7.0f / 16.0f); + } + + if(i < 12) + { + if(i & 3) + fError[i + 3] += fDiff * (3.0f / 16.0f); + + fError[i + 4] += fDiff * (5.0f / 16.0f); + + if(3 != (i & 3)) + { + assert( i < 11 ); + __analysis_assume( i < 11 ); + fError[i + 5] += fDiff * (1.0f / 16.0f); + } + } + } + } + + // RGB part +#ifdef COLOR_WEIGHTS + if(!pBC2->bitmap[0] && !pBC2->bitmap[1]) + { + EncodeSolidBC1(pBC2->dxt1, Color); + return; + } +#endif // COLOR_WEIGHTS + + EncodeBC1(&pBC2->bc1, Color, false, 0.f, flags); +} + + +//------------------------------------------------------------------------------------- +// BC3 Compression +//------------------------------------------------------------------------------------- +void D3DXDecodeBC3(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(D3DX_BC3) == 16, "D3DX_BC3 should be 16 bytes" ); + + const D3DX_BC3 *pBC3 = reinterpret_cast(pBC); + + // RGB part + DecodeBC1(pColor, &pBC3->bc1); + + // Adaptive 3-bit alpha part + float fAlpha[8]; + + fAlpha[0] = ((float) pBC3->alpha[0]) * (1.0f / 255.0f); + fAlpha[1] = ((float) pBC3->alpha[1]) * (1.0f / 255.0f); + + if(pBC3->alpha[0] > pBC3->alpha[1]) + { + for(size_t i = 1; i < 7; ++i) + fAlpha[i + 1] = (fAlpha[0] * (7 - i) + fAlpha[1] * i) * (1.0f / 7.0f); + } + else + { + for(size_t i = 1; i < 5; ++i) + fAlpha[i + 1] = (fAlpha[0] * (5 - i) + fAlpha[1] * i) * (1.0f / 5.0f); + + fAlpha[6] = 0.0f; + fAlpha[7] = 1.0f; + } + + DWORD dw = pBC3->bitmap[0] | (pBC3->bitmap[1] << 8) | (pBC3->bitmap[2] << 16); + + for(size_t i = 0; i < 8; ++i, dw >>= 3) + pColor[i] = XMVectorSetW( pColor[i], fAlpha[dw & 0x7] ); + + dw = pBC3->bitmap[3] | (pBC3->bitmap[4] << 8) | (pBC3->bitmap[5] << 16); + + for(size_t i = 8; i < NUM_PIXELS_PER_BLOCK; ++i, dw >>= 3) + pColor[i] = XMVectorSetW( pColor[i], fAlpha[dw & 0x7] ); +} + +void D3DXEncodeBC3(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags) +{ + assert( pBC && pColor ); + static_assert( sizeof(D3DX_BC3) == 16, "D3DX_BC3 should be 16 bytes" ); + + HDRColorA Color[NUM_PIXELS_PER_BLOCK]; + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + XMStoreFloat4( reinterpret_cast( &Color[i] ), pColor[i] ); + } + + D3DX_BC3 *pBC3 = reinterpret_cast(pBC); + + // Quantize block to A8, using Floyd Stienberg error diffusion. This + // increases the chance that colors will map directly to the quantized + // axis endpoints. + float fAlpha[NUM_PIXELS_PER_BLOCK]; + float fError[NUM_PIXELS_PER_BLOCK]; + + float fMinAlpha = Color[0].a; + float fMaxAlpha = Color[0].a; + + if (flags & BC_FLAGS_DITHER_A) + memset(fError, 0x00, NUM_PIXELS_PER_BLOCK * sizeof(float)); + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + float fAlph = Color[i].a; + if (flags & BC_FLAGS_DITHER_A) + fAlph += fError[i]; + + fAlpha[i] = static_cast(fAlph * 255.0f + 0.5f) * (1.0f / 255.0f); + + if(fAlpha[i] < fMinAlpha) + fMinAlpha = fAlpha[i]; + else if(fAlpha[i] > fMaxAlpha) + fMaxAlpha = fAlpha[i]; + + if (flags & BC_FLAGS_DITHER_A) + { + float fDiff = fAlph - fAlpha[i]; + + if(3 != (i & 3)) + { + assert( i < 15 ); + __analysis_assume( i < 15 ); + fError[i + 1] += fDiff * (7.0f / 16.0f); + } + + if(i < 12) + { + if(i & 3) + fError[i + 3] += fDiff * (3.0f / 16.0f); + + fError[i + 4] += fDiff * (5.0f / 16.0f); + + if(3 != (i & 3)) + { + assert( i < 11 ); + __analysis_assume( i < 11 ); + fError[i + 5] += fDiff * (1.0f / 16.0f); + } + } + } + } + +#ifdef COLOR_WEIGHTS + if(0.0f == fMaxAlpha) + { + EncodeSolidBC1(&pBC3->dxt1, Color); + pBC3->alpha[0] = 0x00; + pBC3->alpha[1] = 0x00; + memset(pBC3->bitmap, 0x00, 6); + } +#endif + + // RGB part + EncodeBC1(&pBC3->bc1, Color, false, 0.f, flags); + + // Alpha part + if(1.0f == fMinAlpha) + { + pBC3->alpha[0] = 0xff; + pBC3->alpha[1] = 0xff; + memset(pBC3->bitmap, 0x00, 6); + return; + } + + // Optimize and Quantize Min and Max values + size_t uSteps = ((0.0f == fMinAlpha) || (1.0f == fMaxAlpha)) ? 6 : 8; + + float fAlphaA, fAlphaB; + OptimizeAlpha(&fAlphaA, &fAlphaB, fAlpha, uSteps); + + uint8_t bAlphaA = (uint8_t) static_cast(fAlphaA * 255.0f + 0.5f); + uint8_t bAlphaB = (uint8_t) static_cast(fAlphaB * 255.0f + 0.5f); + + fAlphaA = (float) bAlphaA * (1.0f / 255.0f); + fAlphaB = (float) bAlphaB * (1.0f / 255.0f); + + // Setup block + if((8 == uSteps) && (bAlphaA == bAlphaB)) + { + pBC3->alpha[0] = bAlphaA; + pBC3->alpha[1] = bAlphaB; + memset(pBC3->bitmap, 0x00, 6); + return; + } + + static const size_t pSteps6[] = { 0, 2, 3, 4, 5, 1 }; + static const size_t pSteps8[] = { 0, 2, 3, 4, 5, 6, 7, 1 }; + + const size_t *pSteps; + float fStep[8]; + + if(6 == uSteps) + { + pBC3->alpha[0] = bAlphaA; + pBC3->alpha[1] = bAlphaB; + + fStep[0] = fAlphaA; + fStep[1] = fAlphaB; + + for(size_t i = 1; i < 5; ++i) + fStep[i + 1] = (fStep[0] * (5 - i) + fStep[1] * i) * (1.0f / 5.0f); + + fStep[6] = 0.0f; + fStep[7] = 1.0f; + + pSteps = pSteps6; + } + else + { + pBC3->alpha[0] = bAlphaB; + pBC3->alpha[1] = bAlphaA; + + fStep[0] = fAlphaB; + fStep[1] = fAlphaA; + + for(size_t i = 1; i < 7; ++i) + fStep[i + 1] = (fStep[0] * (7 - i) + fStep[1] * i) * (1.0f / 7.0f); + + pSteps = pSteps8; + } + + // Encode alpha bitmap + float fSteps = (float) (uSteps - 1); + float fScale = (fStep[0] != fStep[1]) ? (fSteps / (fStep[1] - fStep[0])) : 0.0f; + + if (flags & BC_FLAGS_DITHER_A) + memset(fError, 0x00, NUM_PIXELS_PER_BLOCK * sizeof(float)); + + for(size_t iSet = 0; iSet < 2; iSet++) + { + uint32_t dw = 0; + + size_t iMin = iSet * 8; + size_t iLim = iMin + 8; + + for(size_t i = iMin; i < iLim; ++i) + { + float fAlph = Color[i].a; + if (flags & BC_FLAGS_DITHER_A) + fAlph += fError[i]; + float fDot = (fAlph - fStep[0]) * fScale; + + uint32_t iStep; + if(fDot <= 0.0f) + iStep = ((6 == uSteps) && (fAlph <= fStep[0] * 0.5f)) ? 6 : 0; + else if(fDot >= fSteps) + iStep = ((6 == uSteps) && (fAlph >= (fStep[1] + 1.0f) * 0.5f)) ? 7 : 1; + else + iStep = static_cast( pSteps[static_cast(fDot + 0.5f)] ); + + dw = (iStep << 21) | (dw >> 3); + + if (flags & BC_FLAGS_DITHER_A) + { + float fDiff = (fAlph - fStep[iStep]); + + if(3 != (i & 3)) + fError[i + 1] += fDiff * (7.0f / 16.0f); + + if(i < 12) + { + if(i & 3) + fError[i + 3] += fDiff * (3.0f / 16.0f); + + fError[i + 4] += fDiff * (5.0f / 16.0f); + + if(3 != (i & 3)) + fError[i + 5] += fDiff * (1.0f / 16.0f); + } + } + } + + pBC3->bitmap[0 + iSet * 3] = ((uint8_t *) &dw)[0]; + pBC3->bitmap[1 + iSet * 3] = ((uint8_t *) &dw)[1]; + pBC3->bitmap[2 + iSet * 3] = ((uint8_t *) &dw)[2]; + } +} + +} // namespace \ No newline at end of file diff --git a/DirectXTex/BC.h b/DirectXTex/BC.h new file mode 100644 index 0000000..83e8139 --- /dev/null +++ b/DirectXTex/BC.h @@ -0,0 +1,897 @@ +//------------------------------------------------------------------------------------- +// BC.h +// +// Block-compression (BC) functionality +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#if defined(_MSC_VER) && (_MSC_VER > 1000) +#pragma once +#endif + +#include + +#ifdef USE_XNAMATH +#include +#else +#include +#include +#endif + +#include + +#pragma warning(push) +#pragma warning(disable : 4005) +#include +#pragma warning(pop) + +namespace DirectX +{ + +#ifndef USE_XNAMATH +typedef PackedVector::HALF HALF; +typedef PackedVector::XMHALF4 XMHALF4; +typedef PackedVector::XMU565 XMU565; +#endif + +//------------------------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------------------------- + +const uint16_t F16S_MASK = 0x8000; // f16 sign mask +const uint16_t F16EM_MASK = 0x7fff; // f16 exp & mantissa mask +const uint16_t F16MAX = 0x7bff; // MAXFLT bit pattern for XMHALF + +#define SIGN_EXTEND(x,nb) ((((x)&(1<<((nb)-1)))?((~0)<<(nb)):0)|(x)) + +// Because these are used in SAL annotations, they need to remain macros rather than const values +#define NUM_PIXELS_PER_BLOCK 16 +#define BC6H_MAX_REGIONS 2 +#define BC6H_MAX_INDICES 16 +#define BC7_MAX_REGIONS 3 +#define BC7_MAX_INDICES 16 + +const size_t BC6H_NUM_CHANNELS = 3; +const size_t BC6H_MAX_SHAPES = 32; + +const size_t BC7_NUM_CHANNELS = 4; +const size_t BC7_MAX_SHAPES = 64; + +const uint32_t BC67_WEIGHT_MAX = 64; +const uint32_t BC67_WEIGHT_SHIFT = 6; +const uint32_t BC67_WEIGHT_ROUND = 32; + +extern const int g_aWeights2[4]; +extern const int g_aWeights3[8]; +extern const int g_aWeights4[16]; + +enum BC_FLAGS +{ + BC_FLAGS_NONE = 0x0, + BC_FLAGS_DITHER_RGB = 0x10000, // Enables dithering for RGB colors for BC1-3 + BC_FLAGS_DITHER_A = 0x20000, // Enables dithering for Alpha channel for BC1-3 + BC_FLAGS_UNIFORM = 0x40000, // By default, uses perceptual weighting for BC1-3; this flag makes it a uniform weighting +}; + +//------------------------------------------------------------------------------------- +// Structures +//------------------------------------------------------------------------------------- +class HDRColorA; + +class LDRColorA +{ +public: + uint8_t r, g, b, a; + + LDRColorA() {} + LDRColorA(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a) : r(_r), g(_g), b(_b), a(_a) {} + + const uint8_t& operator [] (_In_range_(0,3) size_t uElement) const + { + switch(uElement) + { + case 0: return r; + case 1: return g; + case 2: return b; + case 3: return a; + default: assert(false); return r; + } + } + + uint8_t& operator [] (_In_range_(0,3) size_t uElement) + { + switch(uElement) + { + case 0: return r; + case 1: return g; + case 2: return b; + case 3: return a; + default: assert(false); return r; + } + } + + LDRColorA operator = (_In_ const HDRColorA& c); + + static void InterpolateRGB(_In_ const LDRColorA& c0, _In_ const LDRColorA& c1, _In_ size_t wc, _In_ size_t wcprec, _Out_ LDRColorA& out) + { + const int* aWeights = nullptr; + switch(wcprec) + { + case 2: aWeights = g_aWeights2; assert( wc < 4 ); __analysis_assume( wc < 4 ); break; + case 3: aWeights = g_aWeights3; assert( wc < 8 ); __analysis_assume( wc < 8 ); break; + case 4: aWeights = g_aWeights4; assert( wc < 16 ); __analysis_assume( wc < 16 ); break; + default: assert(false); out.r = out.g = out.b = 0; return; + } + out.r = uint8_t((uint32_t(c0.r) * uint32_t(BC67_WEIGHT_MAX - aWeights[wc]) + uint32_t(c1.r) * uint32_t(aWeights[wc]) + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT); + out.g = uint8_t((uint32_t(c0.g) * uint32_t(BC67_WEIGHT_MAX - aWeights[wc]) + uint32_t(c1.g) * uint32_t(aWeights[wc]) + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT); + out.b = uint8_t((uint32_t(c0.b) * uint32_t(BC67_WEIGHT_MAX - aWeights[wc]) + uint32_t(c1.b) * uint32_t(aWeights[wc]) + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT); + } + + static void InterpolateA(_In_ const LDRColorA& c0, _In_ const LDRColorA& c1, _In_ size_t wa, _In_ size_t waprec, _Out_ LDRColorA& out) + { + const int* aWeights = nullptr; + switch(waprec) + { + case 2: aWeights = g_aWeights2; assert( wa < 4 ); __analysis_assume( wa < 4 ); break; + case 3: aWeights = g_aWeights3; assert( wa < 8 ); __analysis_assume( wa < 8 ); break; + case 4: aWeights = g_aWeights4; assert( wa < 16 ); __analysis_assume( wa < 16 ); break; + default: assert(false); out.a = 0; return; + } + out.a = uint8_t((uint32_t(c0.a) * uint32_t(BC67_WEIGHT_MAX - aWeights[wa]) + uint32_t(c1.a) * uint32_t(aWeights[wa]) + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT); + } + + static void Interpolate(_In_ const LDRColorA& c0, _In_ const LDRColorA& c1, _In_ size_t wc, _In_ size_t wa, _In_ size_t wcprec, _In_ size_t waprec, _Out_ LDRColorA& out) + { + InterpolateRGB(c0, c1, wc, wcprec, out); + InterpolateA(c0, c1, wa, waprec, out); + } +}; + +class HDRColorA +{ +public: + float r, g, b, a; + +public: + HDRColorA() {} + HDRColorA(float _r, float _g, float _b, float _a) : r(_r), g(_g), b(_b), a(_a) {} + HDRColorA(const HDRColorA& c) : r(c.r), g(c.g), b(c.b), a(c.a) {} + HDRColorA(const LDRColorA& c) + { + r = float(c.r) * (1.0f/255.0f); + g = float(c.g) * (1.0f/255.0f); + b = float(c.b) * (1.0f/255.0f); + a = float(c.a) * (1.0f/255.0f); + } + + // binary operators + HDRColorA operator + ( _In_ const HDRColorA& c ) const + { + return HDRColorA(r + c.r, g + c.g, b + c.b, a + c.a); + } + + HDRColorA operator - ( _In_ const HDRColorA& c ) const + { + return HDRColorA(r - c.r, g - c.g, b - c.b, a - c.a); + } + + HDRColorA operator * ( _In_ float f ) const + { + return HDRColorA(r * f, g * f, b * f, a * f); + } + + HDRColorA operator / ( _In_ float f ) const + { + float fInv = 1.0f / f; + return HDRColorA(r * fInv, g * fInv, b * fInv, a * fInv); + } + + float operator * ( _In_ const HDRColorA& c ) const + { + return r * c.r + g * c.g + b * c.b + a * c.a; + } + + // assignment operators + HDRColorA& operator += ( _In_ const HDRColorA& c ) + { + r += c.r; + g += c.g; + b += c.b; + a += c.a; + return *this; + } + + HDRColorA& operator -= ( _In_ const HDRColorA& c ) + { + r -= c.r; + g -= c.g; + b -= c.b; + a -= c.a; + return *this; + } + + HDRColorA& operator *= ( _In_ float f ) + { + r *= f; + g *= f; + b *= f; + a *= f; + return *this; + } + + HDRColorA& operator /= ( _In_ float f ) + { + float fInv = 1.0f / f; + r *= fInv; + g *= fInv; + b *= fInv; + a *= fInv; + return *this; + } + + HDRColorA& operator = (_In_ const LDRColorA& c) + { + r = (float) c.r; + g = (float) c.g; + b = (float) c.b; + a = (float) c.a; + return *this; + } + + HDRColorA& Clamp(_In_ float fMin, _In_ float fMax) + { + r = std::min(fMax, std::max(fMin, r)); + g = std::min(fMax, std::max(fMin, g)); + b = std::min(fMax, std::max(fMin, b)); + a = std::min(fMax, std::max(fMin, a)); + return *this; + } + + LDRColorA ToLDRColorA() const + { + return LDRColorA((uint8_t) (r + 0.01f), (uint8_t) (g + 0.01f), (uint8_t) (b + 0.01f), (uint8_t) (a + 0.01f)); + } +}; + +inline LDRColorA LDRColorA::operator = (_In_ const HDRColorA& c) +{ + LDRColorA ret; + HDRColorA tmp(c); + tmp = tmp.Clamp(0.0f, 1.0f) * 255.0f; + ret.r = uint8_t(tmp.r + 0.001f); + ret.g = uint8_t(tmp.g + 0.001f); + ret.b = uint8_t(tmp.b + 0.001f); + ret.a = uint8_t(tmp.a + 0.001f); + return ret; +} + +struct LDREndPntPair +{ + LDRColorA A; + LDRColorA B; +}; + +struct HDREndPntPair +{ + HDRColorA A; + HDRColorA B; +}; + +inline HDRColorA* HDRColorALerp(_Out_ HDRColorA *pOut, _In_ const HDRColorA *pC1, _In_ const HDRColorA *pC2, _In_ float s) +{ + pOut->r = pC1->r + s * (pC2->r - pC1->r); + pOut->g = pC1->g + s * (pC2->g - pC1->g); + pOut->b = pC1->b + s * (pC2->b - pC1->b); + pOut->a = pC1->a + s * (pC2->a - pC1->a); + return pOut; +} + +#pragma pack(push,1) +// BC1/DXT1 compression (4 bits per texel) +struct D3DX_BC1 +{ + uint16_t rgb[2]; // 565 colors + uint32_t bitmap; // 2bpp rgb bitmap +}; + +// BC2/DXT2/3 compression (8 bits per texel) +struct D3DX_BC2 +{ + uint32_t bitmap[2]; // 4bpp alpha bitmap + D3DX_BC1 bc1; // BC1 rgb data +}; + +// BC3/DXT4/5 compression (8 bits per texel) +struct D3DX_BC3 +{ + uint8_t alpha[2]; // alpha values + uint8_t bitmap[6]; // 3bpp alpha bitmap + D3DX_BC1 bc1; // BC1 rgb data +}; +#pragma pack(pop) + +class INTColor +{ +public: + int r, g, b; + +public: + INTColor() {} + INTColor(int nr, int ng, int nb) {r = nr; g = ng; b = nb;} + INTColor(const INTColor& c) {r = c.r; g = c.g; b = c.b;} + + INTColor operator - ( _In_ const INTColor& c ) const + { + return INTColor(r - c.r, g - c.g, b - c.b); + } + + INTColor& operator += ( _In_ const INTColor& c ) + { + r += c.r; + g += c.g; + b += c.b; + return *this; + } + + INTColor& operator -= ( _In_ const INTColor& c ) + { + r -= c.r; + g -= c.g; + b -= c.b; + return *this; + } + + INTColor& operator &= ( _In_ const INTColor& c ) + { + r &= c.r; + g &= c.g; + b &= c.b; + return *this; + } + + int& operator [] ( _In_ uint8_t i ) + { + assert(i < sizeof(INTColor) / sizeof(int)); + __analysis_assume(i < sizeof(INTColor) / sizeof(int)); + return ((int*) this)[i]; + } + + void Set(_In_ const HDRColorA& c, _In_ bool bSigned) + { + XMHALF4 aF16; + + XMVECTOR v = XMLoadFloat4( (const XMFLOAT4*)& c ); + XMStoreHalf4( &aF16, v ); + + r = F16ToINT(aF16.x, bSigned); + g = F16ToINT(aF16.y, bSigned); + b = F16ToINT(aF16.z, bSigned); + } + + INTColor& Clamp(_In_ int iMin, _In_ int iMax) + { + r = std::min(iMax, std::max(iMin, r)); + g = std::min(iMax, std::max(iMin, g)); + b = std::min(iMax, std::max(iMin, b)); + return *this; + } + + INTColor& SignExtend(_In_ const LDRColorA& Prec) + { + r = SIGN_EXTEND(r, Prec.r); + g = SIGN_EXTEND(g, Prec.g); + b = SIGN_EXTEND(b, Prec.b); + return *this; + } + + void ToF16(_Out_cap_c_(3) HALF aF16[3], _In_ bool bSigned) const + { + aF16[0] = INT2F16(r, bSigned); + aF16[1] = INT2F16(g, bSigned); + aF16[2] = INT2F16(b, bSigned); + } + +private: + static int F16ToINT(_In_ const HALF& f, _In_ bool bSigned) + { + uint16_t input = *((const uint16_t*) &f); + int out, s; + if(bSigned) + { + s = input & F16S_MASK; + input &= F16EM_MASK; + if(input > F16MAX) out = F16MAX; + else out = input; + out = s ? -out : out; + } + else + { + if(input & F16S_MASK) out = 0; + else out = input; + } + return out; + } + + static HALF INT2F16(_In_ int input, _In_ bool bSigned) + { + HALF h; + uint16_t out; + if(bSigned) + { + int s = 0; + if(input < 0) + { + s = F16S_MASK; + input = -input; + } + out = uint16_t(s | input); + } + else + { + assert(input >= 0 && input <= F16MAX); + out = (uint16_t) input; + } + + *((uint16_t*) &h) = out; + return h; + } +}; + +struct INTEndPntPair +{ + INTColor A; + INTColor B; +}; + +template< size_t SizeInBytes > +class CBits +{ +public: + uint8_t GetBit(_Inout_ size_t& uStartBit) const + { + assert(uStartBit < 128); + __analysis_assume(uStartBit < 128); + size_t uIndex = uStartBit >> 3; + uint8_t ret = (m_uBits[uIndex] >> (uStartBit - (uIndex << 3))) & 0x01; + uStartBit++; + return ret; + } + + uint8_t GetBits(_Inout_ size_t& uStartBit, _In_ size_t uNumBits) const + { + if(uNumBits == 0) return 0; + assert(uStartBit + uNumBits <= 128 && uNumBits <= 8); + __analysis_assume(uStartBit + uNumBits <= 128 && uNumBits <= 8); + uint8_t ret; + size_t uIndex = uStartBit >> 3; + size_t uBase = uStartBit - (uIndex << 3); + if(uBase + uNumBits > 8) + { + size_t uFirstIndexBits = 8 - uBase; + size_t uNextIndexBits = uNumBits - uFirstIndexBits; + ret = (m_uBits[uIndex] >> uBase) | ((m_uBits[uIndex+1] & ((1 << uNextIndexBits) - 1)) << uFirstIndexBits); + } + else + { + ret = (m_uBits[uIndex] >> uBase) & ((1 << uNumBits) - 1); + } + assert(ret < (1 << uNumBits)); + uStartBit += uNumBits; + return ret; + } + + void SetBit(_Inout_ size_t& uStartBit, _In_ uint8_t uValue) + { + assert(uStartBit < 128 && uValue < 2); + __analysis_assume(uStartBit < 128 && uValue < 2); + size_t uIndex = uStartBit >> 3; + size_t uBase = uStartBit - (uIndex << 3); + m_uBits[uIndex] &= ~(1 << uBase); + m_uBits[uIndex] |= uValue << uBase; + uStartBit++; + } + + void SetBits(_Inout_ size_t& uStartBit, _In_ size_t uNumBits, _In_ uint8_t uValue) + { + if(uNumBits == 0) + return; + assert(uStartBit + uNumBits <= 128 && uNumBits <= 8); + __analysis_assume(uStartBit + uNumBits <= 128 && uNumBits <= 8); + assert(uValue < (1 << uNumBits)); + size_t uIndex = uStartBit >> 3; + size_t uBase = uStartBit - (uIndex << 3); + if(uBase + uNumBits > 8) + { + size_t uFirstIndexBits = 8 - uBase; + size_t uNextIndexBits = uNumBits - uFirstIndexBits; + m_uBits[uIndex] &= ~(((1 << uFirstIndexBits) - 1) << uBase); + m_uBits[uIndex] |= uValue << uBase; + m_uBits[uIndex+1] &= ~((1 << uNextIndexBits) - 1); + m_uBits[uIndex+1] |= uValue >> uFirstIndexBits; + } + else + { + m_uBits[uIndex] &= ~(((1 << uNumBits) - 1) << uBase); + m_uBits[uIndex] |= uValue << uBase; + } + uStartBit += uNumBits; + } + +private: + uint8_t m_uBits[ SizeInBytes ]; +}; + +#pragma warning(push) +#pragma warning(disable : 4127 4480 4512) + +// BC6H compression (16 bits per texel) +class D3DX_BC6H : private CBits< 16 > +{ +public: + void Decode(_In_ bool bSigned, _Out_cap_c_(NUM_PIXELS_PER_BLOCK) HDRColorA* pOut) const; + void Encode(_In_ bool bSigned, _In_count_c_(NUM_PIXELS_PER_BLOCK) const HDRColorA* const pIn); + +private: + enum EField : uint8_t + { + NA, // N/A + M, // Mode + D, // Shape + RW, + RX, + RY, + RZ, + GW, + GX, + GY, + GZ, + BW, + BX, + BY, + BZ, + }; + + struct ModeDescriptor + { + EField m_eField; + uint8_t m_uBit; + }; + + struct ModeInfo + { + uint8_t uMode; + uint8_t uPartitions; + bool bTransformed; + uint8_t uIndexPrec; + LDRColorA RGBAPrec[BC6H_MAX_REGIONS][2]; + }; + + struct EncodeParams + { + float fBestErr; + const bool bSigned; + uint8_t uMode; + uint8_t uShape; + const HDRColorA* const aHDRPixels; + INTEndPntPair aUnqEndPts[BC6H_MAX_SHAPES][BC6H_MAX_REGIONS]; + INTColor aIPixels[NUM_PIXELS_PER_BLOCK]; + + EncodeParams(const HDRColorA* const aOriginal, bool bSignedFormat) : + aHDRPixels(aOriginal), fBestErr(FLT_MAX), bSigned(bSignedFormat) + { + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + aIPixels[i].Set(aOriginal[i], bSigned); + } + } + }; + + static int Quantize(_In_ int iValue, _In_ int prec, _In_ bool bSigned); + static int Unquantize(_In_ int comp, _In_ uint8_t uBitsPerComp, _In_ bool bSigned); + static int FinishUnquantize(_In_ int comp, _In_ bool bSigned); + + static bool EndPointsFit(_In_ const EncodeParams* pEP, _In_count_c_(BC6H_MAX_REGIONS) const INTEndPntPair aEndPts[]); + + void GeneratePaletteQuantized(_In_ const EncodeParams* pEP, _In_ const INTEndPntPair& endPts, + _Out_cap_c_(BC6H_MAX_INDICES) INTColor aPalette[]) const; + float MapColorsQuantized(_In_ const EncodeParams* pEP, _In_count_(np) const INTColor aColors[], _In_ size_t np, _In_ const INTEndPntPair &endPts) const; + float PerturbOne(_In_ const EncodeParams* pEP, _In_count_(np) const INTColor aColors[], _In_ size_t np, _In_ uint8_t ch, + _In_ const INTEndPntPair& oldEndPts, _Out_ INTEndPntPair& newEndPts, _In_ float fOldErr, _In_ int do_b) const; + void OptimizeOne(_In_ const EncodeParams* pEP, _In_count_(np) const INTColor aColors[], _In_ size_t np, _In_ float aOrgErr, + _In_ const INTEndPntPair &aOrgEndPts, _Out_ INTEndPntPair &aOptEndPts) const; + void OptimizeEndPoints(_In_ const EncodeParams* pEP, _In_count_c_(BC6H_MAX_REGIONS) const float aOrgErr[], + _In_count_c_(BC6H_MAX_REGIONS) const INTEndPntPair aOrgEndPts[], + _Inout_count_c_(BC6H_MAX_REGIONS) INTEndPntPair aOptEndPts[]) const; + static void SwapIndices(_In_ const EncodeParams* pEP, _Inout_count_c_(BC6H_MAX_REGIONS) INTEndPntPair aEndPts[], + _In_count_c_(NUM_PIXELS_PER_BLOCK) size_t aIndices[]); + void AssignIndices(_In_ const EncodeParams* pEP, _In_count_c_(BC6H_MAX_REGIONS) const INTEndPntPair aEndPts[], + _Out_cap_c_(NUM_PIXELS_PER_BLOCK) size_t aIndices[], + _Out_cap_c_(BC6H_MAX_REGIONS) float aTotErr[]) const; + void QuantizeEndPts(_In_ const EncodeParams* pEP, _Out_cap_c_(BC6H_MAX_REGIONS) INTEndPntPair* qQntEndPts) const; + void EmitBlock(_In_ const EncodeParams* pEP, _In_count_c_(BC6H_MAX_REGIONS) const INTEndPntPair aEndPts[], + _In_count_c_(NUM_PIXELS_PER_BLOCK) const size_t aIndices[]); + void Refine(_Inout_ EncodeParams* pEP); + + static void GeneratePaletteUnquantized(_In_ const EncodeParams* pEP, _In_ size_t uRegion, _Out_cap_c_(BC6H_MAX_INDICES) INTColor aPalette[]); + float MapColors(_In_ const EncodeParams* pEP, _In_ size_t uRegion, _In_ size_t np, _In_count_(np) const size_t* auIndex) const; + float RoughMSE(_Inout_ EncodeParams* pEP) const; + +private: + const static ModeDescriptor ms_aDesc[][82]; + const static ModeInfo ms_aInfo[]; + const static int ms_aModeToInfo[]; +}; + +// BC67 compression (16b bits per texel) +class D3DX_BC7 : private CBits< 16 > +{ +public: + void Decode(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) HDRColorA* pOut) const; + void Encode(_In_count_c_(NUM_PIXELS_PER_BLOCK) const HDRColorA* const pIn); + +private: + struct ModeInfo + { + uint8_t uPartitions; + uint8_t uPartitionBits; + uint8_t uPBits; + uint8_t uRotationBits; + uint8_t uIndexModeBits; + uint8_t uIndexPrec; + uint8_t uIndexPrec2; + LDRColorA RGBAPrec; + LDRColorA RGBAPrecWithP; + }; + + struct EncodeParams + { + uint8_t uMode; + LDREndPntPair aEndPts[BC7_MAX_SHAPES][BC7_MAX_REGIONS]; + LDRColorA aLDRPixels[NUM_PIXELS_PER_BLOCK]; + const HDRColorA* const aHDRPixels; + + EncodeParams(const HDRColorA* const aOriginal) : aHDRPixels(aOriginal) {} + }; + + static uint8_t Quantize(_In_ uint8_t comp, _In_ uint8_t uPrec) + { + assert(0 < uPrec && uPrec <= 8); + uint8_t rnd = (uint8_t) std::min(255, uint16_t(comp) + (1 << (7 - uPrec))); + return rnd >> (8 - uPrec); + } + + static LDRColorA Quantize(_In_ const LDRColorA& c, _In_ const LDRColorA& RGBAPrec) + { + LDRColorA q; + q.r = Quantize(c.r, RGBAPrec.r); + q.g = Quantize(c.g, RGBAPrec.g); + q.b = Quantize(c.b, RGBAPrec.b); + if(RGBAPrec.a) + q.a = Quantize(c.a, RGBAPrec.a); + else + q.a = 255; + return q; + } + + static uint8_t Unquantize(_In_ uint8_t comp, _In_ size_t uPrec) + { + assert(0 < uPrec && uPrec <= 8); + comp = comp << (8 - uPrec); + return comp | (comp >> uPrec); + } + + static LDRColorA Unquantize(_In_ const LDRColorA& c, _In_ const LDRColorA& RGBAPrec) + { + LDRColorA q; + q.r = Unquantize(c.r, RGBAPrec.r); + q.g = Unquantize(c.g, RGBAPrec.g); + q.b = Unquantize(c.b, RGBAPrec.b); + q.a = RGBAPrec.a > 0 ? Unquantize(c.a, RGBAPrec.a) : 255; + return q; + } + + void GeneratePaletteQuantized(_In_ const EncodeParams* pEP, _In_ size_t uIndexMode, _In_ const LDREndPntPair& endpts, + _Out_cap_c_(BC7_MAX_INDICES) LDRColorA aPalette[]) const; + float PerturbOne(_In_ const EncodeParams* pEP, _In_count_(np) const LDRColorA colors[], _In_ size_t np, _In_ size_t uIndexMode, + _In_ size_t ch, _In_ const LDREndPntPair &old_endpts, + _Out_ LDREndPntPair &new_endpts, _In_ float old_err, _In_ uint8_t do_b) const; + void Exhaustive(_In_ const EncodeParams* pEP, _In_count_(np) const LDRColorA aColors[], _In_ size_t np, _In_ size_t uIndexMode, + _In_ size_t ch, _Inout_ float& fOrgErr, _Inout_ LDREndPntPair& optEndPt) const; + void OptimizeOne(_In_ const EncodeParams* pEP, _In_count_(np) const LDRColorA colors[], _In_ size_t np, _In_ size_t uIndexMode, + _In_ float orig_err, _In_ const LDREndPntPair &orig_endpts, _Out_ LDREndPntPair &opt_endpts) const; + void OptimizeEndPoints(_In_ const EncodeParams* pEP, _In_ size_t uShape, _In_ size_t uIndexMode, + _In_count_c_(BC7_MAX_REGIONS) const float orig_err[], + _In_count_c_(BC7_MAX_REGIONS) const LDREndPntPair orig_endpts[], + _Out_cap_c_(BC7_MAX_REGIONS) LDREndPntPair opt_endpts[]) const; + void AssignIndices(_In_ const EncodeParams* pEP, _In_ size_t uShape, _In_ size_t uIndexMode, + _In_count_c_(BC7_MAX_REGIONS) LDREndPntPair endpts[], + _Out_cap_c_(NUM_PIXELS_PER_BLOCK) size_t aIndices[], _Out_cap_c_(NUM_PIXELS_PER_BLOCK) size_t aIndices2[], + _Out_cap_c_(BC7_MAX_REGIONS) float afTotErr[]) const; + void EmitBlock(_In_ const EncodeParams* pEP, _In_ size_t uShape, _In_ size_t uRotation, _In_ size_t uIndexMode, + _In_count_c_(BC7_MAX_REGIONS) const LDREndPntPair aEndPts[], + _In_count_c_(NUM_PIXELS_PER_BLOCK) const size_t aIndex[], + _In_count_c_(NUM_PIXELS_PER_BLOCK) const size_t aIndex2[]); + float Refine(_In_ const EncodeParams* pEP, _In_ size_t uShape, _In_ size_t uRotation, _In_ size_t uIndexMode); + + float MapColors(_In_ const EncodeParams* pEP, _In_count_(np) const LDRColorA aColors[], _In_ size_t np, _In_ size_t uIndexMode, + _In_ const LDREndPntPair& endPts, _In_ float fMinErr) const; + static float RoughMSE(_Inout_ EncodeParams* pEP, _In_ size_t uShape, _In_ size_t uIndexMode); + +private: + const static ModeInfo ms_aInfo[]; +}; + +//------------------------------------------------------------------------------------- +template void OptimizeAlpha(float *pX, float *pY, const float *pPoints, size_t cSteps) +{ + static const float pC6[] = { 5.0f/5.0f, 4.0f/5.0f, 3.0f/5.0f, 2.0f/5.0f, 1.0f/5.0f, 0.0f/5.0f }; + static const float pD6[] = { 0.0f/5.0f, 1.0f/5.0f, 2.0f/5.0f, 3.0f/5.0f, 4.0f/5.0f, 5.0f/5.0f }; + static const float pC8[] = { 7.0f/7.0f, 6.0f/7.0f, 5.0f/7.0f, 4.0f/7.0f, 3.0f/7.0f, 2.0f/7.0f, 1.0f/7.0f, 0.0f/7.0f }; + static const float pD8[] = { 0.0f/7.0f, 1.0f/7.0f, 2.0f/7.0f, 3.0f/7.0f, 4.0f/7.0f, 5.0f/7.0f, 6.0f/7.0f, 7.0f/7.0f }; + + const float *pC = (6 == cSteps) ? pC6 : pC8; + const float *pD = (6 == cSteps) ? pD6 : pD8; + + float MAX_VALUE = 1.0f; + float MIN_VALUE; + if (bRange) + { + MIN_VALUE = -1.0f; + } + else + { + MIN_VALUE = 0.0f; + } + + // Find Min and Max points, as starting point + float fX = MAX_VALUE; + float fY = MIN_VALUE; + + if(8 == cSteps) + { + for(size_t iPoint = 0; iPoint < NUM_PIXELS_PER_BLOCK; iPoint++) + { + if(pPoints[iPoint] < fX) + fX = pPoints[iPoint]; + + if(pPoints[iPoint] > fY) + fY = pPoints[iPoint]; + } + } + else + { + for(size_t iPoint = 0; iPoint < NUM_PIXELS_PER_BLOCK; iPoint++) + { + if(pPoints[iPoint] < fX && pPoints[iPoint] > MIN_VALUE) + fX = pPoints[iPoint]; + + if(pPoints[iPoint] > fY && pPoints[iPoint] < MAX_VALUE) + fY = pPoints[iPoint]; + } + + if (fX == fY) + { + fY = MAX_VALUE; + } + } + + // Use Newton's Method to find local minima of sum-of-squares error. + float fSteps = (float) (cSteps - 1); + + for(size_t iIteration = 0; iIteration < 8; iIteration++) + { + float fScale; + + if((fY - fX) < (1.0f / 256.0f)) + break; + + fScale = fSteps / (fY - fX); + + // Calculate new steps + float pSteps[8]; + + for(size_t iStep = 0; iStep < cSteps; iStep++) + pSteps[iStep] = pC[iStep] * fX + pD[iStep] * fY; + + if(6 == cSteps) + { + pSteps[6] = MIN_VALUE; + pSteps[7] = MAX_VALUE; + } + + // Evaluate function, and derivatives + float dX = 0.0f; + float dY = 0.0f; + float d2X = 0.0f; + float d2Y = 0.0f; + + for(size_t iPoint = 0; iPoint < NUM_PIXELS_PER_BLOCK; iPoint++) + { + float fDot = (pPoints[iPoint] - fX) * fScale; + + size_t iStep; + + if(fDot <= 0.0f) + iStep = ((6 == cSteps) && (pPoints[iPoint] <= fX * 0.5f)) ? 6 : 0; + else if(fDot >= fSteps) + iStep = ((6 == cSteps) && (pPoints[iPoint] >= (fY + 1.0f) * 0.5f)) ? 7 : (cSteps - 1); + else + iStep = static_cast(fDot + 0.5f); + + + if(iStep < cSteps) + { + // D3DX had this computation backwards (pPoints[iPoint] - pSteps[iStep]) + // this fix improves RMS of the alpha component + float fDiff = pSteps[iStep] - pPoints[iPoint]; + + dX += pC[iStep] * fDiff; + d2X += pC[iStep] * pC[iStep]; + + dY += pD[iStep] * fDiff; + d2Y += pD[iStep] * pD[iStep]; + } + } + + // Move endpoints + if(d2X > 0.0f) + fX -= dX / d2X; + + if(d2Y > 0.0f) + fY -= dY / d2Y; + + if(fX > fY) + { + float f = fX; fX = fY; fY = f; + } + + if((dX * dX < (1.0f / 64.0f)) && (dY * dY < (1.0f / 64.0f))) + break; + } + + *pX = (fX < MIN_VALUE) ? MIN_VALUE : (fX > MAX_VALUE) ? MAX_VALUE : fX; + *pY = (fY < MIN_VALUE) ? MIN_VALUE : (fY > MAX_VALUE) ? MAX_VALUE : fY; +} +#pragma warning(pop) + + +//------------------------------------------------------------------------------------- +// Functions +//------------------------------------------------------------------------------------- + +typedef void (*BC_DECODE)(XMVECTOR *pColor, const uint8_t *pBC); +typedef void (*BC_ENCODE)(uint8_t *pDXT, const XMVECTOR *pColor, DWORD flags); + +void D3DXDecodeBC1(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(8) const uint8_t *pBC); +void D3DXDecodeBC2(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(16) const uint8_t *pBC); +void D3DXDecodeBC3(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(16) const uint8_t *pBC); +void D3DXDecodeBC4U(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(8) const uint8_t *pBC); +void D3DXDecodeBC4S(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(8) const uint8_t *pBC); +void D3DXDecodeBC5U(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(16) const uint8_t *pBC); +void D3DXDecodeBC5S(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(16) const uint8_t *pBC); +void D3DXDecodeBC6HU(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(16) const uint8_t *pBC); +void D3DXDecodeBC6HS(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(16) const uint8_t *pBC); +void D3DXDecodeBC7(_Out_cap_c_(NUM_PIXELS_PER_BLOCK) XMVECTOR *pColor, _In_count_c_(16) const uint8_t *pBC); + +void D3DXEncodeBC1(_Out_cap_c_(8) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ float alphaRef, _In_ DWORD flags); + // BC1 requires one additional parameter, so it doesn't match signature of BC_ENCODE above + +void D3DXEncodeBC2(_Out_cap_c_(16) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC3(_Out_cap_c_(16) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC4U(_Out_cap_c_(8) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC4S(_Out_cap_c_(8) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC5U(_Out_cap_c_(16) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC5S(_Out_cap_c_(16) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC6HU(_Out_cap_c_(16) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC6HS(_Out_cap_c_(16) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); +void D3DXEncodeBC7(_Out_cap_c_(16) uint8_t *pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const XMVECTOR *pColor, _In_ DWORD flags); + +}; // namespace diff --git a/DirectXTex/BC4BC5.cpp b/DirectXTex/BC4BC5.cpp new file mode 100644 index 0000000..b0e230b --- /dev/null +++ b/DirectXTex/BC4BC5.cpp @@ -0,0 +1,534 @@ +//------------------------------------------------------------------------------------- +// BC4BC5.cpp +// +// Block-compression (BC) functionality for BC4 and BC5 (DirectX 10 texture compression) +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +#include "BC.h" + +#pragma warning(disable : 4201) + +namespace DirectX +{ + +//------------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------------ + +// Because these are used in SAL annotations, they need to remain macros rather than const values +#define BLOCK_LEN 4 + // length of each block in texel + +#define BLOCK_SIZE (BLOCK_LEN * BLOCK_LEN) + // total texels in a 4x4 block. + +//------------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------------- + +// BC4U/BC5U +struct BC4_UNORM +{ + float R(size_t uOffset) const + { + size_t uIndex = GetIndex(uOffset); + return DecodeFromIndex(uIndex); + } + + float DecodeFromIndex(size_t uIndex) const + { + if (uIndex == 0) + return red_0 / 255.0f; + if (uIndex == 1) + return red_1 / 255.0f; + float fred_0 = red_0 / 255.0f; + float fred_1 = red_1 / 255.0f; + if (red_0 > red_1) + { + uIndex -= 1; + return (fred_0 * (7-uIndex) + fred_1 * uIndex) / 7.0f; + } + else + { + if (uIndex == 6) + return 0.0f; + if (uIndex == 7) + return 1.0f; + uIndex -= 1; + return (fred_0 * (5-uIndex) + fred_1 * uIndex) / 5.0f; + } + } + + size_t GetIndex(size_t uOffset) const + { + return (size_t) ((data >> (3*uOffset + 16)) & 0x07); + } + + void SetIndex(size_t uOffset, size_t uIndex) + { + data &= ~((uint64_t) 0x07 << (3*uOffset + 16)); + data |= ((uint64_t) uIndex << (3*uOffset + 16)); + } + + union + { + struct + { + uint8_t red_0; + uint8_t red_1; + uint8_t indices[6]; + }; + uint64_t data; + }; +}; + +// BC4S/BC5S +struct BC4_SNORM +{ + float R(size_t uOffset) const + { + size_t uIndex = GetIndex(uOffset); + return DecodeFromIndex(uIndex); + } + + float DecodeFromIndex(size_t uIndex) const + { + int8_t sred_0 = (red_0 == -128)? -127 : red_0; + int8_t sred_1 = (red_1 == -128)? -127 : red_1; + + if (uIndex == 0) + return sred_0 / 127.0f; + if (uIndex == 1) + return sred_1 / 127.0f; + float fred_0 = sred_0 / 127.0f; + float fred_1 = sred_1 / 127.0f; + if (red_0 > red_1) + { + uIndex -= 1; + return (fred_0 * (7-uIndex) + fred_1 * uIndex) / 7.0f; + } + else + { + if (uIndex == 6) + return -1.0f; + if (uIndex == 7) + return 1.0f; + uIndex -= 1; + return (fred_0 * (5-uIndex) + fred_1 * uIndex) / 5.0f; + } + } + + size_t GetIndex(size_t uOffset) const + { + return (size_t) ((data >> (3*uOffset + 16)) & 0x07); + } + + void SetIndex(size_t uOffset, size_t uIndex) + { + data &= ~((uint64_t) 0x07 << (3*uOffset + 16)); + data |= ((uint64_t) uIndex << (3*uOffset + 16)); + } + + union + { + struct + { + int8_t red_0; + int8_t red_1; + uint8_t indices[6]; + }; + uint64_t data; + }; +}; + + +//------------------------------------------------------------------------------------- +// Convert a floating point value to an 8-bit SNORM +//------------------------------------------------------------------------------------- +static void inline FloatToSNorm( _In_ float fVal, _Out_ int8_t *piSNorm ) +{ + const uint32_t dwMostNeg = ( 1 << ( 8 * sizeof( int8_t ) - 1 ) ); + + if( _isnan( fVal ) ) + fVal = 0; + else + if( fVal > 1 ) + fVal = 1; // Clamp to 1 + else + if( fVal < -1 ) + fVal = -1; // Clamp to -1 + + fVal = fVal * (int8_t) ( dwMostNeg - 1 ); + + if( fVal >= 0 ) + fVal += .5f; + else + fVal -= .5f; + + *piSNorm = (int8_t) (fVal); +} + + +//------------------------------------------------------------------------------ +static void FindEndPointsBC4U( _In_count_c_(BLOCK_SIZE) const float theTexelsU[], _Out_ uint8_t &endpointU_0, _Out_ uint8_t &endpointU_1) +{ + // The boundary of codec for signed/unsigned format + float MIN_NORM; + float MAX_NORM = 1.0f; + int8_t iStart, iEnd; + size_t i; + + MIN_NORM = 0.0f; + + // Find max/min of input texels + float fBlockMax = theTexelsU[0]; + float fBlockMin = theTexelsU[0]; + for (i = 0; i < BLOCK_SIZE; ++i) + { + if (theTexelsU[i]fBlockMax) + { + fBlockMax = theTexelsU[i]; + } + } + + // If there are boundary values in input texels, Should use 4 block-codec to guarantee + // the exact code of the boundary values. + bool bUsing4BlockCodec = ( MIN_NORM == fBlockMin || MAX_NORM == fBlockMax ); + + // Using Optimize + float fStart, fEnd; + + if (!bUsing4BlockCodec) + { + OptimizeAlpha(&fStart, &fEnd, theTexelsU, 8); + + iStart = (uint8_t) (fStart * 255.0f); + iEnd = (uint8_t) (fEnd * 255.0f); + + endpointU_0 = iEnd; + endpointU_1 = iStart; + } + else + { + OptimizeAlpha(&fStart, &fEnd, theTexelsU, 6); + + iStart = (uint8_t) (fStart * 255.0f); + iEnd = (uint8_t) (fEnd * 255.0f); + + endpointU_1 = iEnd; + endpointU_0 = iStart; + } +} + +static void FindEndPointsBC4S(_In_count_c_(BLOCK_SIZE) const float theTexelsU[], _Out_ int8_t &endpointU_0, _Out_ int8_t &endpointU_1) +{ + // The boundary of codec for signed/unsigned format + float MIN_NORM; + float MAX_NORM = 1.0f; + int8_t iStart, iEnd; + size_t i; + + MIN_NORM = -1.0f; + + // Find max/min of input texels + float fBlockMax = theTexelsU[0]; + float fBlockMin = theTexelsU[0]; + for (i = 0; i < BLOCK_SIZE; ++i) + { + if (theTexelsU[i]fBlockMax) + { + fBlockMax = theTexelsU[i]; + } + } + + // If there are boundary values in input texels, Should use 4 block-codec to guarantee + // the exact code of the boundary values. + bool bUsing4BlockCodec = ( MIN_NORM == fBlockMin || MAX_NORM == fBlockMax ); + + // Using Optimize + float fStart, fEnd; + + if (!bUsing4BlockCodec) + { + OptimizeAlpha(&fStart, &fEnd, theTexelsU, 8); + + FloatToSNorm(fStart, &iStart); + FloatToSNorm(fEnd, &iEnd); + + endpointU_0 = iEnd; + endpointU_1 = iStart; + } + else + { + OptimizeAlpha(&fStart, &fEnd, theTexelsU, 6); + + FloatToSNorm(fStart, &iStart); + FloatToSNorm(fEnd, &iEnd); + + endpointU_1 = iEnd; + endpointU_0 = iStart; + } +} + + +//------------------------------------------------------------------------------ +static inline void FindEndPointsBC5U( _In_count_c_(BLOCK_SIZE) const float theTexelsU[], _In_count_c_(BLOCK_SIZE) const float theTexelsV[], + _Out_ uint8_t &endpointU_0, _Out_ uint8_t &endpointU_1, _Out_ uint8_t &endpointV_0, _Out_ uint8_t &endpointV_1) +{ + //Encoding the U and V channel by BC4 codec separately. + FindEndPointsBC4U( theTexelsU, endpointU_0, endpointU_1); + FindEndPointsBC4U( theTexelsV, endpointV_0, endpointV_1); +} + +static inline void FindEndPointsBC5S( _In_count_c_(BLOCK_SIZE) const float theTexelsU[], _In_count_c_(BLOCK_SIZE) const float theTexelsV[], + _Out_ int8_t &endpointU_0, _Out_ int8_t &endpointU_1, _Out_ int8_t &endpointV_0, _Out_ int8_t &endpointV_1) +{ + //Encoding the U and V channel by BC4 codec separately. + FindEndPointsBC4S( theTexelsU, endpointU_0, endpointU_1); + FindEndPointsBC4S( theTexelsV, endpointV_0, endpointV_1); +} + + +//------------------------------------------------------------------------------ +static void FindClosestUNORM(_Inout_ BC4_UNORM* pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const float theTexelsU[]) +{ + float rGradient[8]; + int i; + for (i = 0; i < 8; ++i) + { + rGradient[i] = pBC->DecodeFromIndex(i); + } + for (i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + size_t uBestIndex = 0; + float fBestDelta = 100000; + for (size_t uIndex = 0; uIndex < 8; uIndex++) + { + float fCurrentDelta = fabsf(rGradient[uIndex]-theTexelsU[i]); + if (fCurrentDelta < fBestDelta) + { + uBestIndex = uIndex; + fBestDelta = fCurrentDelta; + } + } + pBC->SetIndex(i, uBestIndex); + } +} + +static void FindClosestSNORM(_Inout_ BC4_SNORM* pBC, _In_count_c_(NUM_PIXELS_PER_BLOCK) const float theTexelsU[]) +{ + float rGradient[8]; + int i; + for (i = 0; i < 8; ++i) + { + rGradient[i] = pBC->DecodeFromIndex(i); + } + for (i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + size_t uBestIndex = 0; + float fBestDelta = 100000; + for (size_t uIndex = 0; uIndex < 8; uIndex++) + { + float fCurrentDelta = fabsf(rGradient[uIndex]-theTexelsU[i]); + if (fCurrentDelta < fBestDelta) + { + uBestIndex = uIndex; + fBestDelta = fCurrentDelta; + } + } + pBC->SetIndex(i, uBestIndex); + } +} + + +//===================================================================================== +// Entry points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// BC4 Compression +//------------------------------------------------------------------------------------- +void D3DXDecodeBC4U( XMVECTOR *pColor, const uint8_t *pBC ) +{ + assert( pColor && pBC ); + static_assert( sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes" ); + + const BC4_UNORM * pBC4 = reinterpret_cast(pBC); + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + pColor[i] = XMVectorSet( pBC4->R(i), 0, 0, 1.0f); + } +} + +void D3DXDecodeBC4S(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes" ); + + const BC4_SNORM * pBC4 = reinterpret_cast(pBC); + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + pColor[i] = XMVectorSet( pBC4->R(i), 0, 0, 1.0f); + } +} + +void D3DXEncodeBC4U( uint8_t *pBC, const XMVECTOR *pColor, DWORD flags ) +{ + UNREFERENCED_PARAMETER( flags ); + + assert( pBC && pColor ); + static_assert( sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes" ); + + memset(pBC, 0, sizeof(BC4_UNORM)); + BC4_UNORM * pBC4 = reinterpret_cast(pBC); + float theTexelsU[NUM_PIXELS_PER_BLOCK]; + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + theTexelsU[i] = XMVectorGetX( pColor[i] ); + } + + FindEndPointsBC4U(theTexelsU, pBC4->red_0, pBC4->red_1); + FindClosestUNORM(pBC4, theTexelsU); +} + +void D3DXEncodeBC4S( uint8_t *pBC, const XMVECTOR *pColor, DWORD flags ) +{ + UNREFERENCED_PARAMETER( flags ); + + assert( pBC && pColor ); + static_assert( sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes" ); + + memset(pBC, 0, sizeof(BC4_UNORM)); + BC4_SNORM * pBC4 = reinterpret_cast(pBC); + float theTexelsU[NUM_PIXELS_PER_BLOCK]; + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + theTexelsU[i] = XMVectorGetX( pColor[i] ); + } + + FindEndPointsBC4S(theTexelsU, pBC4->red_0, pBC4->red_1); + FindClosestSNORM(pBC4, theTexelsU); +} + + +//------------------------------------------------------------------------------------- +// BC5 Compression +//------------------------------------------------------------------------------------- +void D3DXDecodeBC5U(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes" ); + + const BC4_UNORM * pBCR = reinterpret_cast(pBC); + const BC4_UNORM * pBCG = reinterpret_cast(pBC+sizeof(BC4_UNORM)); + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + pColor[i] = XMVectorSet(pBCR->R(i), pBCG->R(i), 0, 1.0f); + } +} + +void D3DXDecodeBC5S(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes" ); + + const BC4_SNORM * pBCR = reinterpret_cast(pBC); + const BC4_SNORM * pBCG = reinterpret_cast(pBC+sizeof(BC4_SNORM)); + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + pColor[i] = XMVectorSet(pBCR->R(i), pBCG->R(i), 0, 1.0f); + } +} + +void D3DXEncodeBC5U( uint8_t *pBC, const XMVECTOR *pColor, DWORD flags ) +{ + UNREFERENCED_PARAMETER( flags ); + + assert( pBC && pColor ); + static_assert( sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes" ); + + memset(pBC, 0, sizeof(BC4_UNORM)*2); + BC4_UNORM * pBCR = reinterpret_cast(pBC); + BC4_UNORM * pBCG = reinterpret_cast(pBC+sizeof(BC4_UNORM)); + float theTexelsU[NUM_PIXELS_PER_BLOCK]; + float theTexelsV[NUM_PIXELS_PER_BLOCK]; + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + XMFLOAT4A clr; + XMStoreFloat4A( &clr, pColor[i] ); + theTexelsU[i] = clr.x; + theTexelsV[i] = clr.y; + } + + FindEndPointsBC5U( + theTexelsU, + theTexelsV, + pBCR->red_0, + pBCR->red_1, + pBCG->red_0, + pBCG->red_1); + + FindClosestUNORM(pBCR, theTexelsU); + FindClosestUNORM(pBCG, theTexelsV); +} + +void D3DXEncodeBC5S( uint8_t *pBC, const XMVECTOR *pColor, DWORD flags ) +{ + UNREFERENCED_PARAMETER( flags ); + + assert( pBC && pColor ); + static_assert( sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes" ); + + memset(pBC, 0, sizeof(BC4_UNORM)*2); + BC4_SNORM * pBCR = reinterpret_cast(pBC); + BC4_SNORM * pBCG = reinterpret_cast(pBC+sizeof(BC4_SNORM)); + float theTexelsU[NUM_PIXELS_PER_BLOCK]; + float theTexelsV[NUM_PIXELS_PER_BLOCK]; + + for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + XMFLOAT4A clr; + XMStoreFloat4A( &clr, pColor[i] ); + theTexelsU[i] = clr.x; + theTexelsV[i] = clr.y; + } + + FindEndPointsBC5S( + theTexelsU, + theTexelsV, + pBCR->red_0, + pBCR->red_1, + pBCG->red_0, + pBCG->red_1); + + FindClosestSNORM(pBCR, theTexelsU); + FindClosestSNORM(pBCG, theTexelsV); +} + +} // namespace \ No newline at end of file diff --git a/DirectXTex/BC6HBC7.cpp b/DirectXTex/BC6HBC7.cpp new file mode 100644 index 0000000..02b8c5e --- /dev/null +++ b/DirectXTex/BC6HBC7.cpp @@ -0,0 +1,2819 @@ +//------------------------------------------------------------------------------------- +// BC6HBC7.cpp +// +// Block-compression (BC) functionality for BC6H and BC7 (DirectX 11 texture compression) +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +#include "BC.h" + +#ifndef USE_XNAMATH +using namespace DirectX::PackedVector; +#endif + +namespace DirectX +{ + +//------------------------------------------------------------------------------------- +// Constants +//------------------------------------------------------------------------------------- + +static const float fEpsilon = (0.25f / 64.0f) * (0.25f / 64.0f); +static const float pC3[] = { 2.0f/2.0f, 1.0f/2.0f, 0.0f/2.0f }; +static const float pD3[] = { 0.0f/2.0f, 1.0f/2.0f, 2.0f/2.0f }; +static const float pC4[] = { 3.0f/3.0f, 2.0f/3.0f, 1.0f/3.0f, 0.0f/3.0f }; +static const float pD4[] = { 0.0f/3.0f, 1.0f/3.0f, 2.0f/3.0f, 3.0f/3.0f }; + +const int g_aWeights2[] = {0, 21, 43, 64}; +const int g_aWeights3[] = {0, 9, 18, 27, 37, 46, 55, 64}; +const int g_aWeights4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64}; + +// Partition, Shape, Pixel (index into 4x4 block) +static const uint8_t g_aPartitionTable[3][64][16] = +{ + { // 1 Region case has no subsets (all 0) + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }, + + { // BC6H/BC7 Partition Set for 2 Subsets + { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 }, // Shape 0 + { 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }, // Shape 1 + { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 }, // Shape 2 + { 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1 }, // Shape 3 + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1 }, // Shape 4 + { 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, // Shape 5 + { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, // Shape 6 + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1 }, // Shape 7 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1 }, // Shape 8 + { 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 9 + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, // Shape 10 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 }, // Shape 11 + { 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 12 + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 13 + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 14 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // Shape 15 + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1 }, // Shape 16 + { 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Shape 17 + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0 }, // Shape 18 + { 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 }, // Shape 19 + { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Shape 20 + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 }, // Shape 21 + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // Shape 22 + { 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1 }, // Shape 23 + { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 }, // Shape 24 + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // Shape 25 + { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0 }, // Shape 26 + { 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0 }, // Shape 27 + { 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 }, // Shape 28 + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, // Shape 29 + { 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0 }, // Shape 30 + { 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0 }, // Shape 31 + + // BC7 Partition Set for 2 Subsets (second-half) + { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, // Shape 32 + { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 }, // Shape 33 + { 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0 }, // Shape 34 + { 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 }, // Shape 35 + { 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 }, // Shape 36 + { 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 }, // Shape 37 + { 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 }, // Shape 38 + { 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1 }, // Shape 39 + { 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0 }, // Shape 40 + { 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 }, // Shape 41 + { 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0 }, // Shape 42 + { 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 }, // Shape 43 + { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }, // Shape 44 + { 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1 }, // Shape 45 + { 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 }, // Shape 46 + { 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // Shape 47 + { 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // Shape 48 + { 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // Shape 49 + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0 }, // Shape 50 + { 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0 }, // Shape 51 + { 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1 }, // Shape 52 + { 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1 }, // Shape 53 + { 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0 }, // Shape 54 + { 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0 }, // Shape 55 + { 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1 }, // Shape 56 + { 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 }, // Shape 57 + { 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 }, // Shape 58 + { 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 }, // Shape 59 + { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 }, // Shape 60 + { 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, // Shape 61 + { 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0 }, // Shape 62 + { 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 } // Shape 63 + }, + + { // BC7 Partition Set for 3 Subsets + { 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2 }, // Shape 0 + { 0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1 }, // Shape 1 + { 0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1 }, // Shape 2 + { 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1 }, // Shape 3 + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2 }, // Shape 4 + { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2 }, // Shape 5 + { 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 6 + { 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1 }, // Shape 7 + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2 }, // Shape 8 + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 }, // Shape 9 + { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 10 + { 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2 }, // Shape 11 + { 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2 }, // Shape 12 + { 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2 }, // Shape 13 + { 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2 }, // Shape 14 + { 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0 }, // Shape 15 + { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2 }, // Shape 16 + { 0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0 }, // Shape 17 + { 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2 }, // Shape 18 + { 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1 }, // Shape 19 + { 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2 }, // Shape 20 + { 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1 }, // Shape 21 + { 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2 }, // Shape 22 + { 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0 }, // Shape 23 + { 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0 }, // Shape 24 + { 0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2 }, // Shape 25 + { 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0 }, // Shape 26 + { 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1 }, // Shape 27 + { 0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2 }, // Shape 28 + { 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2 }, // Shape 29 + { 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1 }, // Shape 30 + { 0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1 }, // Shape 31 + { 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2 }, // Shape 32 + { 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1 }, // Shape 33 + { 0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2 }, // Shape 34 + { 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0 }, // Shape 35 + { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0 }, // Shape 36 + { 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0 }, // Shape 37 + { 0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0 }, // Shape 38 + { 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1 }, // Shape 39 + { 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1 }, // Shape 40 + { 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 41 + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1 }, // Shape 42 + { 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2 }, // Shape 43 + { 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1 }, // Shape 44 + { 0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1 }, // Shape 45 + { 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1 }, // Shape 46 + { 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 }, // Shape 47 + { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2 }, // Shape 48 + { 0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1 }, // Shape 49 + { 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2 }, // Shape 50 + { 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2 }, // Shape 51 + { 0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2 }, // Shape 52 + { 0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2 }, // Shape 53 + { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2 }, // Shape 54 + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2 }, // Shape 55 + { 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 56 + { 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2 }, // Shape 57 + { 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2 }, // Shape 58 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2 }, // Shape 59 + { 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1 }, // Shape 60 + { 0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2 }, // Shape 61 + { 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 62 + { 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0 } // Shape 63 + } +}; + +// Partition, Shape, Fixup +static const uint8_t g_aFixUp[3][64][3] = +{ + { // No fix-ups for 1st subset for BC6H or BC7 + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0} + }, + + { // BC6H/BC7 Partition Set Fixups for 2 Subsets + { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, + { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, + { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, + { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, + { 0,15, 0}, { 0, 2, 0}, { 0, 8, 0}, { 0, 2, 0}, + { 0, 2, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0,15, 0}, + { 0, 2, 0}, { 0, 8, 0}, { 0, 2, 0}, { 0, 2, 0}, + { 0, 8, 0}, { 0, 8, 0}, { 0, 2, 0}, { 0, 2, 0}, + + // BC7 Partition Set Fixups for 2 Subsets (second-half) + { 0,15, 0}, { 0,15, 0}, { 0, 6, 0}, { 0, 8, 0}, + { 0, 2, 0}, { 0, 8, 0}, { 0,15, 0}, { 0,15, 0}, + { 0, 2, 0}, { 0, 8, 0}, { 0, 2, 0}, { 0, 2, 0}, + { 0, 2, 0}, { 0,15, 0}, { 0,15, 0}, { 0, 6, 0}, + { 0, 6, 0}, { 0, 2, 0}, { 0, 6, 0}, { 0, 8, 0}, + { 0,15, 0}, { 0,15, 0}, { 0, 2, 0}, { 0, 2, 0}, + { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, { 0,15, 0}, + { 0,15, 0}, { 0, 2, 0}, { 0, 2, 0}, { 0,15, 0} + }, + + { // BC7 Partition Set Fixups for 3 Subsets + { 0, 3,15}, { 0, 3, 8}, { 0,15, 8}, { 0,15, 3}, + { 0, 8,15}, { 0, 3,15}, { 0,15, 3}, { 0,15, 8}, + { 0, 8,15}, { 0, 8,15}, { 0, 6,15}, { 0, 6,15}, + { 0, 6,15}, { 0, 5,15}, { 0, 3,15}, { 0, 3, 8}, + { 0, 3,15}, { 0, 3, 8}, { 0, 8,15}, { 0,15, 3}, + { 0, 3,15}, { 0, 3, 8}, { 0, 6,15}, { 0,10, 8}, + { 0, 5, 3}, { 0, 8,15}, { 0, 8, 6}, { 0, 6,10}, + { 0, 8,15}, { 0, 5,15}, { 0,15,10}, { 0,15, 8}, + { 0, 8,15}, { 0,15, 3}, { 0, 3,15}, { 0, 5,10}, + { 0, 6,10}, { 0,10, 8}, { 0, 8, 9}, { 0,15,10}, + { 0,15, 6}, { 0, 3,15}, { 0,15, 8}, { 0, 5,15}, + { 0,15, 3}, { 0,15, 6}, { 0,15, 6}, { 0,15, 8}, + { 0, 3,15}, { 0,15, 3}, { 0, 5,15}, { 0, 5,15}, + { 0, 5,15}, { 0, 8,15}, { 0, 5,15}, { 0,10,15}, + { 0, 5,15}, { 0,10,15}, { 0, 8,15}, { 0,13,15}, + { 0,15, 3}, { 0,12,15}, { 0, 3,15}, { 0, 3, 8} + } +}; + +// BC6H Compression +const D3DX_BC6H::ModeDescriptor D3DX_BC6H::ms_aDesc[14][82] = +{ + { // 0x00 - 10 5 5 5 + { M, 0}, { M, 1}, {GY, 4}, {BY, 4}, {BZ, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {GZ, 4}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {BZ, 0}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BZ, 1}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {BZ, 2}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {BZ, 3}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x01 - 7 6 6 6 + { M, 0}, { M, 1}, {GY, 5}, {GZ, 4}, {GZ, 5}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {BZ, 0}, {BZ, 1}, {BY, 4}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {BY, 5}, {BZ, 2}, {GY, 4}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BZ, 3}, {BZ, 5}, {BZ, 4}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {RX, 5}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {GX, 5}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BX, 5}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {RY, 5}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x02 - 11 5 4 4 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {RW,10}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GW,10}, + {BZ, 0}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BW,10}, + {BZ, 1}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {BZ, 2}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {BZ, 3}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x06 - 11 4 5 4 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RW,10}, + {GZ, 4}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {GW,10}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BW,10}, + {BZ, 1}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {BZ, 0}, + {BZ, 2}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {GY, 4}, {BZ, 3}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x0a - 11 4 4 5 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RW,10}, + {BY, 4}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GW,10}, + {BZ, 0}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BW,10}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {BZ, 1}, + {BZ, 2}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {BZ, 4}, {BZ, 3}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x0e - 9 5 5 5 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {BY, 4}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GY, 4}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BZ, 4}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {GZ, 4}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {BZ, 0}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BZ, 1}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {BZ, 2}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {BZ, 3}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x12 - 8 6 5 5 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {GZ, 4}, {BY, 4}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {BZ, 2}, {GY, 4}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BZ, 3}, {BZ, 4}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {RX, 5}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {BZ, 0}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BZ, 1}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {RY, 5}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x16 - 8 5 6 5 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {BZ, 0}, {BY, 4}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GY, 5}, {GY, 4}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {GZ, 5}, {BZ, 4}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {GZ, 4}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {GX, 5}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BZ, 1}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {BZ, 2}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {BZ, 3}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x1a - 8 5 5 6 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {BZ, 1}, {BY, 4}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {BY, 5}, {GY, 4}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BZ, 5}, {BZ, 4}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {GZ, 4}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {BZ, 0}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BX, 5}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {BZ, 2}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {BZ, 3}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x1e - 6 6 6 6 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {GZ, 4}, {BZ, 0}, {BZ, 1}, {BY, 4}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GY, 5}, {BY, 5}, {BZ, 2}, {GY, 4}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {GZ, 5}, {BZ, 3}, {BZ, 5}, {BZ, 4}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {RX, 5}, {GY, 0}, {GY, 1}, {GY, 2}, {GY, 3}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {GX, 5}, {GZ, 0}, {GZ, 1}, {GZ, 2}, {GZ, 3}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BX, 5}, {BY, 0}, {BY, 1}, {BY, 2}, {BY, 3}, {RY, 0}, {RY, 1}, {RY, 2}, {RY, 3}, {RY, 4}, + {RY, 5}, {RZ, 0}, {RZ, 1}, {RZ, 2}, {RZ, 3}, {RZ, 4}, {RZ, 5}, { D, 0}, { D, 1}, { D, 2}, + { D, 3}, { D, 4}, + }, + + { // 0x03 - 10 10 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {RX, 5}, {RX, 6}, {RX, 7}, {RX, 8}, {RX, 9}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {GX, 5}, {GX, 6}, {GX, 7}, {GX, 8}, {GX, 9}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BX, 5}, {BX, 6}, {BX, 7}, {BX, 8}, {BX, 9}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, + }, + + { // 0x07 - 11 9 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {RX, 5}, {RX, 6}, {RX, 7}, {RX, 8}, {RW,10}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {GX, 5}, {GX, 6}, {GX, 7}, {GX, 8}, {GW,10}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BX, 5}, {BX, 6}, {BX, 7}, {BX, 8}, {BW,10}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, + }, + + { // 0x0b - 12 8 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RX, 4}, + {RX, 5}, {RX, 6}, {RX, 7}, {RW,11}, {RW,10}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GX, 4}, + {GX, 5}, {GX, 6}, {GX, 7}, {GW,11}, {GW,10}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BX, 4}, + {BX, 5}, {BX, 6}, {BX, 7}, {BW,11}, {BW,10}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, + }, + + { // 0x0f - 16 4 + { M, 0}, { M, 1}, { M, 2}, { M, 3}, { M, 4}, {RW, 0}, {RW, 1}, {RW, 2}, {RW, 3}, {RW, 4}, + {RW, 5}, {RW, 6}, {RW, 7}, {RW, 8}, {RW, 9}, {GW, 0}, {GW, 1}, {GW, 2}, {GW, 3}, {GW, 4}, + {GW, 5}, {GW, 6}, {GW, 7}, {GW, 8}, {GW, 9}, {BW, 0}, {BW, 1}, {BW, 2}, {BW, 3}, {BW, 4}, + {BW, 5}, {BW, 6}, {BW, 7}, {BW, 8}, {BW, 9}, {RX, 0}, {RX, 1}, {RX, 2}, {RX, 3}, {RW,15}, + {RW,14}, {RW,13}, {RW,12}, {RW,11}, {RW,10}, {GX, 0}, {GX, 1}, {GX, 2}, {GX, 3}, {GW,15}, + {GW,14}, {GW,13}, {GW,12}, {GW,11}, {GW,10}, {BX, 0}, {BX, 1}, {BX, 2}, {BX, 3}, {BW,15}, + {BW,14}, {BW,13}, {BW,12}, {BW,11}, {BW,10}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, {NA, 0}, + {NA, 0}, {NA, 0}, + }, +}; + +// Mode, Partitions, Transformed, IndexPrec, RGBAPrec +const D3DX_BC6H::ModeInfo D3DX_BC6H::ms_aInfo[] = +{ + {0x00, 1, true, 3, LDRColorA(10,10,10,0), LDRColorA( 5, 5, 5,0), LDRColorA(5,5,5,0), LDRColorA(5,5,5,0)}, // Mode 0 + {0x01, 1, true, 3, LDRColorA( 7, 7, 7,0), LDRColorA( 6, 6, 6,0), LDRColorA(6,6,6,0), LDRColorA(6,6,6,0)}, // Mode 1 + {0x02, 1, true, 3, LDRColorA(11,11,11,0), LDRColorA( 5, 4, 4,0), LDRColorA(5,4,4,0), LDRColorA(5,4,4,0)}, // Mode 2 + {0x06, 1, true, 3, LDRColorA(11,11,11,0), LDRColorA( 4, 5, 4,0), LDRColorA(4,5,4,0), LDRColorA(4,5,4,0)}, // Mode 3 + {0x0a, 1, true, 3, LDRColorA(11,11,11,0), LDRColorA( 4, 4, 5,0), LDRColorA(4,4,5,0), LDRColorA(4,4,5,0)}, // Mode 4 + {0x0e, 1, true, 3, LDRColorA( 9, 9, 9,0), LDRColorA( 5, 5, 5,0), LDRColorA(5,5,5,0), LDRColorA(5,5,5,0)}, // Mode 5 + {0x12, 1, true, 3, LDRColorA( 8, 8, 8,0), LDRColorA( 6, 5, 5,0), LDRColorA(6,5,5,0), LDRColorA(6,5,5,0)}, // Mode 6 + {0x16, 1, true, 3, LDRColorA( 8, 8, 8,0), LDRColorA( 5, 6, 5,0), LDRColorA(5,6,5,0), LDRColorA(5,6,5,0)}, // Mode 7 + {0x1a, 1, true, 3, LDRColorA( 8, 8, 8,0), LDRColorA( 5, 5, 6,0), LDRColorA(5,5,6,0), LDRColorA(5,5,6,0)}, // Mode 8 + {0x1e, 1, false, 3, LDRColorA( 6, 6, 6,0), LDRColorA( 6, 6, 6,0), LDRColorA(6,6,6,0), LDRColorA(6,6,6,0)}, // Mode 9 + {0x03, 0, false, 4, LDRColorA(10,10,10,0), LDRColorA(10,10,10,0), LDRColorA(0,0,0,0), LDRColorA(0,0,0,0)}, // Mode 10 + {0x07, 0, true, 4, LDRColorA(11,11,11,0), LDRColorA( 9, 9, 9,0), LDRColorA(0,0,0,0), LDRColorA(0,0,0,0)}, // Mode 11 + {0x0b, 0, true, 4, LDRColorA(12,12,12,0), LDRColorA( 8, 8, 8,0), LDRColorA(0,0,0,0), LDRColorA(0,0,0,0)}, // Mode 12 + {0x0f, 0, true, 4, LDRColorA(16,16,16,0), LDRColorA( 4, 4, 4,0), LDRColorA(0,0,0,0), LDRColorA(0,0,0,0)}, // Mode 13 +}; + +const int D3DX_BC6H::ms_aModeToInfo[] = +{ + 0, // 0x00 + 1, // 0x01 + 2, // 0x02 + 10, // 0x03 + -1, // 0x04 + -1, // 0x05 + 3, // 0x06 + 11, // 0x07 + -1, // 0x08 + -1, // 0x09 + 4, // 0x0a + 12, // 0x0b + -1, // 0x0c + -1, // 0x0d + 5, // 0x0e + 13, // 0x0f + -1, // 0x10 + -1, // 0x11 + 6, // 0x12 + -1, // 0x13 + -1, // 0x14 + -1, // 0x15 + 7, // 0x16 + -1, // 0x17 + -1, // 0x18 + -1, // 0x19 + 8, // 0x1a + -1, // 0x1b + -1, // 0x1c + -1, // 0x1d + 9, // 0x1e + -1, // 0x1f +}; + +// BC7 compression: uPartitions, uPartitionBits, uPBits, uRotationBits, uIndexModeBits, uIndexPrec, uIndexPrec2, RGBAPrec, RGBAPrecWithP +const D3DX_BC7::ModeInfo D3DX_BC7::ms_aInfo[] = +{ + {2, 4, 6, 0, 0, 3, 0, LDRColorA(4,4,4,0), LDRColorA(5,5,5,0)}, + // Mode 0: Color only, 3 Subsets, RGBP 4441 (unique P-bit), 3-bit indecies, 16 partitions + {1, 6, 2, 0, 0, 3, 0, LDRColorA(6,6,6,0), LDRColorA(7,7,7,0)}, + // Mode 1: Color only, 2 Subsets, RGBP 6661 (shared P-bit), 3-bit indecies, 64 partitions + {2, 6, 0, 0, 0, 2, 0, LDRColorA(5,5,5,0), LDRColorA(5,5,5,0)}, + // Mode 2: Color only, 3 Subsets, RGB 555, 2-bit indecies, 64 partitions + {1, 6, 4, 0, 0, 2, 0, LDRColorA(7,7,7,0), LDRColorA(8,8,8,0)}, + // Mode 3: Color only, 2 Subsets, RGBP 7771 (unique P-bit), 2-bits indecies, 64 partitions + {0, 0, 0, 2, 1, 2, 3, LDRColorA(5,5,5,6), LDRColorA(5,5,5,6)}, + // Mode 4: Color w/ Separate Alpha, 1 Subset, RGB 555, A6, 16x2/16x3-bit indices, 2-bit rotation, 1-bit index selector + {0, 0, 0, 2, 0, 2, 2, LDRColorA(7,7,7,8), LDRColorA(7,7,7,8)}, + // Mode 5: Color w/ Separate Alpha, 1 Subset, RGB 777, A8, 16x2/16x2-bit indices, 2-bit rotation + {0, 0, 2, 0, 0, 4, 0, LDRColorA(7,7,7,7), LDRColorA(8,8,8,8)}, + // Mode 6: Color+Alpha, 1 Subset, RGBAP 77771 (unique P-bit), 16x4-bit indecies + {1, 6, 4, 0, 0, 2, 0, LDRColorA(5,5,5,5), LDRColorA(6,6,6,6)} + // Mode 7: Color+Alpha, 2 Subsets, RGBAP 55551 (unique P-bit), 2-bit indices, 64 partitions +}; + + +//------------------------------------------------------------------------------------- +// Helper functions +//------------------------------------------------------------------------------------- +template< class T > +inline static void Swap( T& a, T& b ) +{ + T temp = a; + a = b; + b = temp; +} + +inline static bool IsFixUpOffset(_In_range_(0,2) size_t uPartitions, _In_range_(0,63) size_t uShape, _In_range_(0,15) size_t uOffset) +{ + assert(uPartitions < 3 && uShape < 64 && uOffset < 16); + __analysis_assume(uPartitions < 3 && uShape < 64 && uOffset < 16); + for(size_t p = 0; p <= uPartitions; p++) + { + if(uOffset == g_aFixUp[uPartitions][uShape][p]) + { + return true; + } + } + return false; +} + +inline static float ErrorMetricRGB(_In_ const LDRColorA& a, _In_ const LDRColorA& b) +{ + float er = float(a.r) - float(b.r); + float eg = float(a.g) - float(b.g); + float eb = float(a.b) - float(b.b); + // weigh the components nonuniformly + //er *= 0.299; + //eg *= 0.587; + //eb *= 0.114; + return er*er + eg*eg + eb*eb; +} + +inline static float ErrorMetricAlpha(_In_ const LDRColorA& a, _In_ const LDRColorA& b) +{ + float ea = float(a.a) - float(b.a); + return ea*ea; +} + +inline static float ErrorMetric(_In_ const LDRColorA& a, _In_ const LDRColorA& b) +{ + return ErrorMetricRGB(a, b) + ErrorMetricAlpha(a, b); +} + +inline static void TransformForward(_Inout_count_c_(BC6H_MAX_REGIONS) INTEndPntPair aEndPts[]) +{ + aEndPts[0].B -= aEndPts[0].A; + aEndPts[1].A -= aEndPts[0].A; + aEndPts[1].B -= aEndPts[0].A; +} + +inline static void TransformInverse(_Inout_count_c_(BC6H_MAX_REGIONS) INTEndPntPair aEndPts[], _In_ const LDRColorA& Prec, _In_ bool bSigned) +{ + INTColor WrapMask((1 << Prec.r) - 1, (1 << Prec.g) - 1, (1 << Prec.b) - 1); + aEndPts[0].B += aEndPts[0].A; aEndPts[0].B &= WrapMask; + aEndPts[1].A += aEndPts[0].A; aEndPts[1].A &= WrapMask; + aEndPts[1].B += aEndPts[0].A; aEndPts[1].B &= WrapMask; + if(bSigned) + { + aEndPts[0].B.SignExtend(Prec); + aEndPts[1].A.SignExtend(Prec); + aEndPts[1].B.SignExtend(Prec); + } +} + +inline static float Norm(_In_ const INTColor& a, _In_ const INTColor& b) +{ + float dr = float(a.r) - float(b.r); + float dg = float(a.g) - float(b.g); + float db = float(a.b) - float(b.b); + return dr * dr + dg * dg + db * db; +} + +// return # of bits needed to store n. handle signed or unsigned cases properly +inline static int NBits(_In_ int n, _In_ bool bIsSigned) +{ + int nb; + if(n == 0) + { + return 0; // no bits needed for 0, signed or not + } + else if(n > 0) + { + for(nb = 0; n; ++nb, n >>= 1); + return nb + (bIsSigned ? 1 : 0); + } + else + { + assert(bIsSigned); + for(nb = 0; n < -1; ++nb, n >>= 1) ; + return nb + 1; + } +} + + +//------------------------------------------------------------------------------------- +static float OptimizeRGB(_In_count_c_(NUM_PIXELS_PER_BLOCK) const HDRColorA* const pPoints, + _Out_ HDRColorA* pX, _Out_ HDRColorA* pY, + _In_ size_t cSteps, _In_ size_t cPixels, _In_count_(cPixels) const size_t* pIndex) +{ + float fError = FLT_MAX; + const float *pC = (3 == cSteps) ? pC3 : pC4; + const float *pD = (3 == cSteps) ? pD3 : pD4; + + // Find Min and Max points, as starting point + HDRColorA X(1.0f, 1.0f, 1.0f, 0.0f); + HDRColorA Y(0.0f, 0.0f, 0.0f, 0.0f); + + for(size_t iPoint = 0; iPoint < cPixels; iPoint++) + { + if(pPoints[pIndex[iPoint]].r < X.r) X.r = pPoints[pIndex[iPoint]].r; + if(pPoints[pIndex[iPoint]].g < X.g) X.g = pPoints[pIndex[iPoint]].g; + if(pPoints[pIndex[iPoint]].b < X.b) X.b = pPoints[pIndex[iPoint]].b; + if(pPoints[pIndex[iPoint]].r > Y.r) Y.r = pPoints[pIndex[iPoint]].r; + if(pPoints[pIndex[iPoint]].g > Y.g) Y.g = pPoints[pIndex[iPoint]].g; + if(pPoints[pIndex[iPoint]].b > Y.b) Y.b = pPoints[pIndex[iPoint]].b; + } + + // Diagonal axis + HDRColorA AB; + AB.r = Y.r - X.r; + AB.g = Y.g - X.g; + AB.b = Y.b - X.b; + + float fAB = AB.r * AB.r + AB.g * AB.g + AB.b * AB.b; + + // Single color block.. no need to root-find + if(fAB < FLT_MIN) + { + pX->r = X.r; pX->g = X.g; pX->b = X.b; + pY->r = Y.r; pY->g = Y.g; pY->b = Y.b; + return 0.0f; + } + + // Try all four axis directions, to determine which diagonal best fits data + float fABInv = 1.0f / fAB; + + HDRColorA Dir; + Dir.r = AB.r * fABInv; + Dir.g = AB.g * fABInv; + Dir.b = AB.b * fABInv; + + HDRColorA Mid; + Mid.r = (X.r + Y.r) * 0.5f; + Mid.g = (X.g + Y.g) * 0.5f; + Mid.b = (X.b + Y.b) * 0.5f; + + float fDir[4]; + fDir[0] = fDir[1] = fDir[2] = fDir[3] = 0.0f; + + for(size_t iPoint = 0; iPoint < cPixels; iPoint++) + { + HDRColorA Pt; + Pt.r = (pPoints[pIndex[iPoint]].r - Mid.r) * Dir.r; + Pt.g = (pPoints[pIndex[iPoint]].g - Mid.g) * Dir.g; + Pt.b = (pPoints[pIndex[iPoint]].b - Mid.b) * Dir.b; + + float f; + f = Pt.r + Pt.g + Pt.b; fDir[0] += f * f; + f = Pt.r + Pt.g - Pt.b; fDir[1] += f * f; + f = Pt.r - Pt.g + Pt.b; fDir[2] += f * f; + f = Pt.r - Pt.g - Pt.b; fDir[3] += f * f; + } + + float fDirMax = fDir[0]; + size_t iDirMax = 0; + + for(size_t iDir = 1; iDir < 4; iDir++) + { + if(fDir[iDir] > fDirMax) + { + fDirMax = fDir[iDir]; + iDirMax = iDir; + } + } + + if(iDirMax & 2) Swap( X.g, Y.g ); + if(iDirMax & 1) Swap( X.b, Y.b ); + + // Two color block.. no need to root-find + if(fAB < 1.0f / 4096.0f) + { + pX->r = X.r; pX->g = X.g; pX->b = X.b; + pY->r = Y.r; pY->g = Y.g; pY->b = Y.b; + return 0.0f; + } + + // Use Newton's Method to find local minima of sum-of-squares error. + float fSteps = (float) (cSteps - 1); + + for(size_t iIteration = 0; iIteration < 8; iIteration++) + { + // Calculate new steps + HDRColorA pSteps[4]; + + for(size_t iStep = 0; iStep < cSteps; iStep++) + { + pSteps[iStep].r = X.r * pC[iStep] + Y.r * pD[iStep]; + pSteps[iStep].g = X.g * pC[iStep] + Y.g * pD[iStep]; + pSteps[iStep].b = X.b * pC[iStep] + Y.b * pD[iStep]; + } + + // Calculate color direction + Dir.r = Y.r - X.r; + Dir.g = Y.g - X.g; + Dir.b = Y.b - X.b; + + float fLen = (Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b); + + if(fLen < (1.0f / 4096.0f)) + break; + + float fScale = fSteps / fLen; + + Dir.r *= fScale; + Dir.g *= fScale; + Dir.b *= fScale; + + // Evaluate function, and derivatives + float d2X = 0.0f, d2Y = 0.0f; + HDRColorA dX(0.0f, 0.0f, 0.0f, 0.0f), dY(0.0f, 0.0f, 0.0f, 0.0f); + + for(size_t iPoint = 0; iPoint < cPixels; iPoint++) + { + float fDot = (pPoints[pIndex[iPoint]].r - X.r) * Dir.r + + (pPoints[pIndex[iPoint]].g - X.g) * Dir.g + + (pPoints[pIndex[iPoint]].b - X.b) * Dir.b; + + size_t iStep; + if(fDot <= 0.0f) + iStep = 0; + if(fDot >= fSteps) + iStep = cSteps - 1; + else + iStep = size_t(fDot + 0.5f); + + HDRColorA Diff; + Diff.r = pSteps[iStep].r - pPoints[pIndex[iPoint]].r; + Diff.g = pSteps[iStep].g - pPoints[pIndex[iPoint]].g; + Diff.b = pSteps[iStep].b - pPoints[pIndex[iPoint]].b; + + float fC = pC[iStep] * (1.0f / 8.0f); + float fD = pD[iStep] * (1.0f / 8.0f); + + d2X += fC * pC[iStep]; + dX.r += fC * Diff.r; + dX.g += fC * Diff.g; + dX.b += fC * Diff.b; + + d2Y += fD * pD[iStep]; + dY.r += fD * Diff.r; + dY.g += fD * Diff.g; + dY.b += fD * Diff.b; + } + + // Move endpoints + if(d2X > 0.0f) + { + float f = -1.0f / d2X; + + X.r += dX.r * f; + X.g += dX.g * f; + X.b += dX.b * f; + } + + if(d2Y > 0.0f) + { + float f = -1.0f / d2Y; + + Y.r += dY.r * f; + Y.g += dY.g * f; + Y.b += dY.b * f; + } + + if((dX.r * dX.r < fEpsilon) && (dX.g * dX.g < fEpsilon) && (dX.b * dX.b < fEpsilon) && + (dY.r * dY.r < fEpsilon) && (dY.g * dY.g < fEpsilon) && (dY.b * dY.b < fEpsilon)) + { + break; + } + } + + pX->r = X.r; pX->g = X.g; pX->b = X.b; + pY->r = Y.r; pY->g = Y.g; pY->b = Y.b; + return fError; +} + + +//------------------------------------------------------------------------------------- +static float OptimizeRGBA(_In_count_c_(NUM_PIXELS_PER_BLOCK) const HDRColorA* const pPoints, + _Out_ HDRColorA* pX, _Out_ HDRColorA* pY, + _In_ size_t cSteps, _In_ size_t cPixels, _In_count_(cPixels) const size_t* pIndex) +{ + float fError = FLT_MAX; + const float *pC = (3 == cSteps) ? pC3 : pC4; + const float *pD = (3 == cSteps) ? pD3 : pD4; + + // Find Min and Max points, as starting point + HDRColorA X(1.0f, 1.0f, 1.0f, 1.0f); + HDRColorA Y(0.0f, 0.0f, 0.0f, 0.0f); + + for(size_t iPoint = 0; iPoint < cPixels; iPoint++) + { + if(pPoints[pIndex[iPoint]].r < X.r) X.r = pPoints[pIndex[iPoint]].r; + if(pPoints[pIndex[iPoint]].g < X.g) X.g = pPoints[pIndex[iPoint]].g; + if(pPoints[pIndex[iPoint]].b < X.b) X.b = pPoints[pIndex[iPoint]].b; + if(pPoints[pIndex[iPoint]].a < X.a) X.a = pPoints[pIndex[iPoint]].a; + if(pPoints[pIndex[iPoint]].r > Y.r) Y.r = pPoints[pIndex[iPoint]].r; + if(pPoints[pIndex[iPoint]].g > Y.g) Y.g = pPoints[pIndex[iPoint]].g; + if(pPoints[pIndex[iPoint]].b > Y.b) Y.b = pPoints[pIndex[iPoint]].b; + if(pPoints[pIndex[iPoint]].a > Y.a) Y.a = pPoints[pIndex[iPoint]].a; + } + + // Diagonal axis + HDRColorA AB = Y - X; + float fAB = AB * AB; + + // Single color block.. no need to root-find + if(fAB < FLT_MIN) + { + *pX = X; + *pY = Y; + return 0.0f; + } + + // Try all four axis directions, to determine which diagonal best fits data + float fABInv = 1.0f / fAB; + HDRColorA Dir = AB * fABInv; + HDRColorA Mid = (X + Y) * 0.5f; + + float fDir[8]; + fDir[0] = fDir[1] = fDir[2] = fDir[3] = fDir[4] = fDir[5] = fDir[6] = fDir[7] = 0.0f; + + for(size_t iPoint = 0; iPoint < cPixels; iPoint++) + { + HDRColorA Pt; + Pt.r = (pPoints[pIndex[iPoint]].r - Mid.r) * Dir.r; + Pt.g = (pPoints[pIndex[iPoint]].g - Mid.g) * Dir.g; + Pt.b = (pPoints[pIndex[iPoint]].b - Mid.b) * Dir.b; + Pt.a = (pPoints[pIndex[iPoint]].a - Mid.a) * Dir.a; + + float f; + f = Pt.r + Pt.g + Pt.b + Pt.a; fDir[0] += f * f; + f = Pt.r + Pt.g + Pt.b - Pt.a; fDir[1] += f * f; + f = Pt.r + Pt.g - Pt.b + Pt.a; fDir[2] += f * f; + f = Pt.r + Pt.g - Pt.b - Pt.a; fDir[3] += f * f; + f = Pt.r - Pt.g + Pt.b + Pt.a; fDir[4] += f * f; + f = Pt.r - Pt.g + Pt.b - Pt.a; fDir[5] += f * f; + f = Pt.r - Pt.g - Pt.b + Pt.a; fDir[6] += f * f; + f = Pt.r - Pt.g - Pt.b - Pt.a; fDir[7] += f * f; + } + + float fDirMax = fDir[0]; + size_t iDirMax = 0; + + for(size_t iDir = 1; iDir < 8; iDir++) + { + if(fDir[iDir] > fDirMax) + { + fDirMax = fDir[iDir]; + iDirMax = iDir; + } + } + + if(iDirMax & 4) Swap(X.g, Y.g); + if(iDirMax & 2) Swap(X.b, Y.b); + if(iDirMax & 1) Swap(X.a, Y.a); + + // Two color block.. no need to root-find + if(fAB < 1.0f / 4096.0f) + { + *pX = X; + *pY = Y; + return 0.0f; + } + + // Use Newton's Method to find local minima of sum-of-squares error. + float fSteps = (float) (cSteps - 1); + + for(size_t iIteration = 0; iIteration < 8 && fError > 0.0f; iIteration++) + { + // Calculate new steps + HDRColorA pSteps[BC7_MAX_INDICES]; + + LDRColorA aSteps[BC7_MAX_INDICES]; + LDRColorA lX, lY; + lX = (X * 255.0f).ToLDRColorA(); + lY = (Y * 255.0f).ToLDRColorA(); + + for(size_t iStep = 0; iStep < cSteps; iStep++) + { + pSteps[iStep] = X * pC[iStep] + Y * pD[iStep]; + //LDRColorA::Interpolate(lX, lY, i, i, wcprec, waprec, aSteps[i]); + } + + // Calculate color direction + Dir = Y - X; + float fLen = Dir * Dir; + if(fLen < (1.0f / 4096.0f)) + break; + + float fScale = fSteps / fLen; + Dir *= fScale; + + // Evaluate function, and derivatives + float d2X = 0.0f, d2Y = 0.0f; + HDRColorA dX(0.0f, 0.0f, 0.0f, 0.0f), dY(0.0f, 0.0f, 0.0f, 0.0f); + + for(size_t iPoint = 0; iPoint < cPixels; ++iPoint) + { + float fDot = (pPoints[pIndex[iPoint]] - X) * Dir; + size_t iStep; + if(fDot <= 0.0f) + iStep = 0; + if(fDot >= fSteps) + iStep = cSteps - 1; + else + iStep = size_t(fDot + 0.5f); + + HDRColorA Diff = pSteps[iStep] - pPoints[pIndex[iPoint]]; + float fC = pC[iStep] * (1.0f / 8.0f); + float fD = pD[iStep] * (1.0f / 8.0f); + + d2X += fC * pC[iStep]; + dX += Diff * fC; + + d2Y += fD * pD[iStep]; + dY += Diff * fD; + } + + // Move endpoints + if(d2X > 0.0f) + { + float f = -1.0f / d2X; + X += dX * f; + } + + if(d2Y > 0.0f) + { + float f = -1.0f / d2Y; + Y += dY * f; + } + + if((dX * dX < fEpsilon) && (dY * dY < fEpsilon)) + break; + } + + *pX = X; + *pY = Y; + return fError; +} + + +//------------------------------------------------------------------------------------- +#pragma warning(disable: 4616 6001 6297) + +static float ComputeError(_Inout_ const LDRColorA& pixel, _In_count_x_(1 << uIndexPrec) const LDRColorA aPalette[], + _In_ uint8_t uIndexPrec, _In_ uint8_t uIndexPrec2, _Out_opt_ size_t* pBestIndex = nullptr, _Out_opt_ size_t* pBestIndex2 = nullptr) +{ + const size_t uNumIndices = 1 << uIndexPrec; + const size_t uNumIndices2 = 1 << uIndexPrec2; + float fTotalErr = 0; + float fBestErr = FLT_MAX; + + if(pBestIndex) + *pBestIndex = 0; + if(pBestIndex2) + *pBestIndex2 = 0; + + if(uIndexPrec2 == 0) + { + for(register size_t i = 0; i < uNumIndices && fBestErr > 0; i++) + { + float fErr = ErrorMetric(pixel, aPalette[i]); + if(fErr > fBestErr) // error increased, so we're done searching + break; + if(fErr < fBestErr) + { + fBestErr = fErr; + if(pBestIndex) + *pBestIndex = i; + } + } + fTotalErr += fBestErr; + } + else + { + for(register size_t i = 0; i < uNumIndices && fBestErr > 0; i++) + { + float fErr = ErrorMetricRGB(pixel, aPalette[i]); + if(fErr > fBestErr) // error increased, so we're done searching + break; + if(fErr < fBestErr) + { + fBestErr = fErr; + if(pBestIndex) + *pBestIndex = i; + } + } + fTotalErr += fBestErr; + fBestErr = FLT_MAX; + for(register size_t i = 0; i < uNumIndices2 && fBestErr > 0; i++) + { + float fErr = ErrorMetricAlpha(pixel, aPalette[i]); + if(fErr > fBestErr) // error increased, so we're done searching + break; + if(fErr < fBestErr) + { + fBestErr = fErr; + if(pBestIndex2) + *pBestIndex2 = i; + } + } + fTotalErr += fBestErr; + } + + return fTotalErr; +} + + +inline static void FillWithErrorColors( _Out_cap_c_(NUM_PIXELS_PER_BLOCK) HDRColorA* pOut ) +{ + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { +#ifdef DEBUG + // Use Magenta in debug as a highly-visible error color + pOut[i] = HDRColorA(1.0f, 0.0f, 1.0f, 1.0f); +#else + // In production use, default to black + pOut[i] = HDRColorA(0.0f, 0.0f, 0.0f, 1.0f); +#endif + } +} + + +//------------------------------------------------------------------------------------- +// BC6H Compression +//------------------------------------------------------------------------------------- +void D3DX_BC6H::Decode(bool bSigned, HDRColorA* pOut) const +{ + assert(pOut ); + + size_t uStartBit = 0; + uint8_t uMode = GetBits(uStartBit, 2); + if(uMode != 0x00 && uMode != 0x01) + { + uMode = (GetBits(uStartBit, 3) << 2) | uMode; + } + + assert( uMode < 32 ); + __analysis_assume( uMode < 32 ); + + if ( ms_aModeToInfo[uMode] >= 0 ) + { + assert(ms_aModeToInfo[uMode] < ARRAYSIZE(ms_aInfo)); + __analysis_assume(ms_aModeToInfo[uMode] < ARRAYSIZE(ms_aInfo)); + const ModeDescriptor* desc = ms_aDesc[ms_aModeToInfo[uMode]]; + + assert(ms_aModeToInfo[uMode] < ARRAYSIZE(ms_aDesc)); + __analysis_assume(ms_aModeToInfo[uMode] < ARRAYSIZE(ms_aDesc)); + const ModeInfo& info = ms_aInfo[ms_aModeToInfo[uMode]]; + + INTEndPntPair aEndPts[BC6H_MAX_REGIONS]; + memset(aEndPts, 0, BC6H_MAX_REGIONS * 2 * sizeof(INTColor)); + uint32_t uShape = 0; + + // Read header + const size_t uHeaderBits = info.uPartitions > 0 ? 82 : 65; + while(uStartBit < uHeaderBits) + { + size_t uCurBit = uStartBit; + if(GetBit(uStartBit)) + { + switch(desc[uCurBit].m_eField) + { + case D: uShape |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case RW: aEndPts[0].A.r |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case RX: aEndPts[0].B.r |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case RY: aEndPts[1].A.r |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case RZ: aEndPts[1].B.r |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case GW: aEndPts[0].A.g |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case GX: aEndPts[0].B.g |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case GY: aEndPts[1].A.g |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case GZ: aEndPts[1].B.g |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case BW: aEndPts[0].A.b |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case BX: aEndPts[0].B.b |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case BY: aEndPts[1].A.b |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + case BZ: aEndPts[1].B.b |= 1 << uint32_t(desc[uCurBit].m_uBit); break; + default: + { +#ifdef DEBUG + OutputDebugStringA( "BC6H: Invalid header bits encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + } + } + } + + assert( uShape < 64 ); + __analysis_assume( uShape < 64 ); + + // Sign extend necessary end points + if(bSigned) + { + aEndPts[0].A.SignExtend(info.RGBAPrec[0][0]); + } + if(bSigned || info.bTransformed) + { + assert( info.uPartitions < BC6H_MAX_REGIONS ); + __analysis_assume( info.uPartitions < BC6H_MAX_REGIONS ); + for(size_t p = 0; p <= info.uPartitions; ++p) + { + if(p != 0) + { + aEndPts[p].A.SignExtend(info.RGBAPrec[p][0]); + } + aEndPts[p].B.SignExtend(info.RGBAPrec[p][1]); + } + } + + // Inverse transform the end points + if(info.bTransformed) + { + TransformInverse(aEndPts, info.RGBAPrec[0][0], bSigned); + } + + // Read indices + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + size_t uNumBits = IsFixUpOffset(info.uPartitions, uShape, i) ? info.uIndexPrec-1 : info.uIndexPrec; + if ( uStartBit + uNumBits > 128 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC6H: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + uint8_t uIndex = GetBits(uStartBit, uNumBits); + + if ( uIndex >= ((info.uPartitions > 0) ? 8 : 16) ) + { +#ifdef DEBUG + OutputDebugStringA( "BC6H: Invalid index encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + + size_t uRegion = g_aPartitionTable[info.uPartitions][uShape][i]; + assert( uRegion < BC6H_MAX_REGIONS ); + __analysis_assume( uRegion < BC6H_MAX_REGIONS ); + + // Unquantize endpoints and interpolate + int r1 = Unquantize(aEndPts[uRegion].A.r, info.RGBAPrec[0][0].r, bSigned); + int g1 = Unquantize(aEndPts[uRegion].A.g, info.RGBAPrec[0][0].g, bSigned); + int b1 = Unquantize(aEndPts[uRegion].A.b, info.RGBAPrec[0][0].b, bSigned); + int r2 = Unquantize(aEndPts[uRegion].B.r, info.RGBAPrec[0][0].r, bSigned); + int g2 = Unquantize(aEndPts[uRegion].B.g, info.RGBAPrec[0][0].g, bSigned); + int b2 = Unquantize(aEndPts[uRegion].B.b, info.RGBAPrec[0][0].b, bSigned); + const int* aWeights = info.uPartitions > 0 ? g_aWeights3 : g_aWeights4; + INTColor fc; + fc.r = FinishUnquantize((r1 * (BC67_WEIGHT_MAX - aWeights[uIndex]) + r2 * aWeights[uIndex] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT, bSigned); + fc.g = FinishUnquantize((g1 * (BC67_WEIGHT_MAX - aWeights[uIndex]) + g2 * aWeights[uIndex] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT, bSigned); + fc.b = FinishUnquantize((b1 * (BC67_WEIGHT_MAX - aWeights[uIndex]) + b2 * aWeights[uIndex] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT, bSigned); + + HALF rgb[3]; + fc.ToF16(rgb, bSigned); + + pOut[i].r = XMConvertHalfToFloat( rgb[0] ); + pOut[i].g = XMConvertHalfToFloat( rgb[1] ); + pOut[i].b = XMConvertHalfToFloat( rgb[2] ); + pOut[i].a = 1.0f; + } + } + else + { +#ifdef DEBUG + OutputDebugStringA( "BC6H: Invalid mode encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + } +} + +void D3DX_BC6H::Encode(bool bSigned, const HDRColorA* const pIn) +{ + assert( pIn ); + + EncodeParams EP(pIn, bSigned); + + for(EP.uMode = 0; EP.uMode < ARRAYSIZE(ms_aInfo) && EP.fBestErr > 0; ++EP.uMode) + { + const uint8_t uShapes = ms_aInfo[EP.uMode].uPartitions ? 32 : 1; + // Number of rough cases to look at. reasonable values of this are 1, uShapes/4, and uShapes + // uShapes/4 gets nearly all the cases; you can increase that a bit (say by 3 or 4) if you really want to squeeze the last bit out + const size_t uItems = std::max(1, uShapes >> 2); + float afRoughMSE[BC6H_MAX_SHAPES]; + uint8_t auShape[BC6H_MAX_SHAPES]; + + // pick the best uItems shapes and refine these. + for(EP.uShape = 0; EP.uShape < uShapes; ++EP.uShape) + { + size_t uShape = EP.uShape; // make prefast happy + afRoughMSE[uShape] = RoughMSE(&EP); + auShape[uShape] = static_cast(uShape); + } + + // Bubble up the first uItems items + for(register size_t i = 0; i < uItems; i++) + { + for(register size_t j = i + 1; j < uShapes; j++) + { + if(afRoughMSE[i] > afRoughMSE[j]) + { + Swap(afRoughMSE[i], afRoughMSE[j]); + Swap(auShape[i], auShape[j]); + } + } + } + + for(size_t i = 0; i < uItems && EP.fBestErr > 0; i++) + { + EP.uShape = auShape[i]; + Refine(&EP); + } + } +} + + +//------------------------------------------------------------------------------------- +int D3DX_BC6H::Quantize(int iValue, int prec, bool bSigned) +{ + assert(prec > 1); // didn't bother to make it work for 1 + int q, s = 0; + if(bSigned) + { + assert(iValue >= -F16MAX && iValue <= F16MAX); + if(iValue < 0) + { + s = 1; + iValue = -iValue; + } + q = (prec >= 16) ? iValue : (iValue << (prec-1)) / (F16MAX+1); + if(s) + q = -q; + assert (q > -(1 << (prec-1)) && q < (1 << (prec-1))); + } + else + { + assert(iValue >= 0 && iValue <= F16MAX); + q = (prec >= 15) ? iValue : (iValue << prec) / (F16MAX+1); + assert (q >= 0 && q < (1 << prec)); + } + + return q; +} + +int D3DX_BC6H::Unquantize(int comp, uint8_t uBitsPerComp, bool bSigned) +{ + int unq = 0, s = 0; + if(bSigned) + { + if(uBitsPerComp >= 16) + { + unq = comp; + } + else + { + if(comp < 0) + { + s = 1; + comp = -comp; + } + + if(comp == 0) unq = 0; + else if(comp >= ((1 << (uBitsPerComp - 1)) - 1)) unq = 0x7FFF; + else unq = ((comp << 15) + 0x4000) >> (uBitsPerComp-1); + + if(s) unq = -unq; + } + } + else + { + if(uBitsPerComp >= 15) unq = comp; + else if(comp == 0) unq = 0; + else if(comp == ((1 << uBitsPerComp) - 1)) unq = 0xFFFF; + else unq = ((comp << 16) + 0x8000) >> uBitsPerComp; + } + + return unq; +} + +int D3DX_BC6H::FinishUnquantize(int comp, bool bSigned) +{ + if(bSigned) + { + return (comp < 0) ? -(((-comp) * 31) >> 5) : (comp * 31) >> 5; // scale the magnitude by 31/32 + } + else + { + return (comp * 31) >> 6; // scale the magnitude by 31/64 + } +} + + +//------------------------------------------------------------------------------------- +bool D3DX_BC6H::EndPointsFit(const EncodeParams* pEP, const INTEndPntPair aEndPts[]) +{ + assert( pEP ); + const bool bTransformed = ms_aInfo[pEP->uMode].bTransformed; + const bool bIsSigned = pEP->bSigned; + const LDRColorA& Prec0 = ms_aInfo[pEP->uMode].RGBAPrec[0][0]; + const LDRColorA& Prec1 = ms_aInfo[pEP->uMode].RGBAPrec[0][1]; + const LDRColorA& Prec2 = ms_aInfo[pEP->uMode].RGBAPrec[1][0]; + const LDRColorA& Prec3 = ms_aInfo[pEP->uMode].RGBAPrec[1][1]; + + INTColor aBits[4]; + aBits[0].r = NBits(aEndPts[0].A.r, bIsSigned); + aBits[0].g = NBits(aEndPts[0].A.g, bIsSigned); + aBits[0].b = NBits(aEndPts[0].A.b, bIsSigned); + aBits[1].r = NBits(aEndPts[0].B.r, bTransformed || bIsSigned); + aBits[1].g = NBits(aEndPts[0].B.g, bTransformed || bIsSigned); + aBits[1].b = NBits(aEndPts[0].B.b, bTransformed || bIsSigned); + if(aBits[0].r > Prec0.r || aBits[1].r > Prec1.r || + aBits[0].g > Prec0.g || aBits[1].g > Prec1.g || + aBits[0].b > Prec0.b || aBits[1].b > Prec1.b) + return false; + + if(ms_aInfo[pEP->uMode].uPartitions) + { + aBits[2].r = NBits(aEndPts[1].A.r, bTransformed || bIsSigned); + aBits[2].g = NBits(aEndPts[1].A.g, bTransformed || bIsSigned); + aBits[2].b = NBits(aEndPts[1].A.b, bTransformed || bIsSigned); + aBits[3].r = NBits(aEndPts[1].B.r, bTransformed || bIsSigned); + aBits[3].g = NBits(aEndPts[1].B.g, bTransformed || bIsSigned); + aBits[3].b = NBits(aEndPts[1].B.b, bTransformed || bIsSigned); + + if(aBits[2].r > Prec2.r || aBits[3].r > Prec3.r || + aBits[2].g > Prec2.g || aBits[3].g > Prec3.g || + aBits[2].b > Prec2.b || aBits[3].b > Prec3.b) + return false; + } + + return true; +} + +void D3DX_BC6H::GeneratePaletteQuantized(const EncodeParams* pEP, const INTEndPntPair& endPts, INTColor aPalette[]) const +{ + assert( pEP ); + const size_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; + const size_t uNumIndices = 1 << uIndexPrec; + assert( uNumIndices > 0 ); + __analysis_assume( uNumIndices > 0 ); + const LDRColorA& Prec = ms_aInfo[pEP->uMode].RGBAPrec[0][0]; + + // scale endpoints + INTEndPntPair unqEndPts; + unqEndPts.A.r = Unquantize(endPts.A.r, Prec.r, pEP->bSigned); + unqEndPts.A.g = Unquantize(endPts.A.g, Prec.g, pEP->bSigned); + unqEndPts.A.b = Unquantize(endPts.A.b, Prec.b, pEP->bSigned); + unqEndPts.B.r = Unquantize(endPts.B.r, Prec.r, pEP->bSigned); + unqEndPts.B.g = Unquantize(endPts.B.g, Prec.g, pEP->bSigned); + unqEndPts.B.b = Unquantize(endPts.B.b, Prec.b, pEP->bSigned); + + // interpolate + const int* aWeights = nullptr; + switch(uIndexPrec) + { + case 3: aWeights = g_aWeights3; assert(uNumIndices <= 8); __analysis_assume(uNumIndices <= 8); break; + case 4: aWeights = g_aWeights4; assert(uNumIndices <= 16); __analysis_assume(uNumIndices <= 16); break; + default: assert(false); for(size_t i=0; i < uNumIndices; ++i) aPalette[i] = INTColor(0,0,0); return; + } + + for (size_t i = 0; i < uNumIndices; ++i) + { + aPalette[i].r = FinishUnquantize( + (unqEndPts.A.r * (BC67_WEIGHT_MAX - aWeights[i]) + unqEndPts.B.r * aWeights[i] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT, + pEP->bSigned); + aPalette[i].g = FinishUnquantize( + (unqEndPts.A.g * (BC67_WEIGHT_MAX - aWeights[i]) + unqEndPts.B.g * aWeights[i] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT, + pEP->bSigned); + aPalette[i].b = FinishUnquantize( + (unqEndPts.A.b * (BC67_WEIGHT_MAX - aWeights[i]) + unqEndPts.B.b * aWeights[i] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT, + pEP->bSigned); + } +} + +// given a collection of colors and quantized endpoints, generate a palette, choose best entries, and return a single toterr +float D3DX_BC6H::MapColorsQuantized(const EncodeParams* pEP, const INTColor aColors[], size_t np, const INTEndPntPair &endPts) const +{ + assert( pEP ); + + const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; + const uint8_t uNumIndices = 1 << uIndexPrec; + INTColor aPalette[BC6H_MAX_INDICES]; + GeneratePaletteQuantized(pEP, endPts, aPalette); + + float fTotErr = 0; + for(size_t i = 0; i < np; ++i) + { + float fBestErr = Norm(aColors[i], aPalette[0]); + for(int j = 1; j < uNumIndices && fBestErr > 0; ++j) + { + float fErr = Norm(aColors[i], aPalette[j]); + if(fErr > fBestErr) break; // error increased, so we're done searching + if(fErr < fBestErr) fBestErr = fErr; + } + fTotErr += fBestErr; + } + return fTotErr; +} + +float D3DX_BC6H::PerturbOne(const EncodeParams* pEP, const INTColor aColors[], size_t np, uint8_t ch, + const INTEndPntPair& oldEndPts, INTEndPntPair& newEndPts, float fOldErr, int do_b) const +{ + assert( pEP ); + uint8_t uPrec; + switch(ch) + { + case 0: uPrec = ms_aInfo[pEP->uMode].RGBAPrec[0][0].r; break; + case 1: uPrec = ms_aInfo[pEP->uMode].RGBAPrec[0][0].g; break; + case 2: uPrec = ms_aInfo[pEP->uMode].RGBAPrec[0][0].b; break; + default: assert(false); newEndPts = oldEndPts; return FLT_MAX; + } + INTEndPntPair tmpEndPts; + float fMinErr = fOldErr; + int beststep = 0; + + // copy real endpoints so we can perturb them + tmpEndPts = newEndPts = oldEndPts; + + // do a logarithmic search for the best error for this endpoint (which) + for(int step = 1 << (uPrec-1); step; step >>= 1) + { + bool bImproved = false; + for(int sign = -1; sign <= 1; sign += 2) + { + if(do_b == 0) + { + tmpEndPts.A[ch] = newEndPts.A[ch] + sign * step; + if(tmpEndPts.A[ch] < 0 || tmpEndPts.A[ch] >= (1 << uPrec)) + continue; + } + else + { + tmpEndPts.B[ch] = newEndPts.B[ch] + sign * step; + if(tmpEndPts.B[ch] < 0 || tmpEndPts.B[ch] >= (1 << uPrec)) + continue; + } + + float fErr = MapColorsQuantized(pEP, aColors, np, tmpEndPts); + + if(fErr < fMinErr) + { + bImproved = true; + fMinErr = fErr; + beststep = sign * step; + } + } + // if this was an improvement, move the endpoint and continue search from there + if(bImproved) + { + if(do_b == 0) + newEndPts.A[ch] += beststep; + else + newEndPts.B[ch] += beststep; + } + } + return fMinErr; +} + +void D3DX_BC6H::OptimizeOne(const EncodeParams* pEP, const INTColor aColors[], size_t np, float aOrgErr, + const INTEndPntPair &aOrgEndPts, INTEndPntPair &aOptEndPts) const +{ + assert( pEP ); + float aOptErr = aOrgErr; + aOptEndPts.A = aOrgEndPts.A; + aOptEndPts.B = aOrgEndPts.B; + + INTEndPntPair new_a, new_b; + INTEndPntPair newEndPts; + int do_b; + + // now optimize each channel separately + for(uint8_t ch = 0; ch < 3; ++ch) + { + // figure out which endpoint when perturbed gives the most improvement and start there + // if we just alternate, we can easily end up in a local minima + float fErr0 = PerturbOne(pEP, aColors, np, ch, aOptEndPts, new_a, aOptErr, 0); // perturb endpt A + float fErr1 = PerturbOne(pEP, aColors, np, ch, aOptEndPts, new_b, aOptErr, 1); // perturb endpt B + + if(fErr0 < fErr1) + { + if(fErr0 >= aOptErr) continue; + aOptEndPts.A[ch] = new_a.A[ch]; + aOptErr = fErr0; + do_b = 1; // do B next + } + else + { + if(fErr1 >= aOptErr) continue; + aOptEndPts.B[ch] = new_b.B[ch]; + aOptErr = fErr1; + do_b = 0; // do A next + } + + // now alternate endpoints and keep trying until there is no improvement + for(;;) + { + float fErr = PerturbOne(pEP, aColors, np, ch, aOptEndPts, newEndPts, aOptErr, do_b); + if(fErr >= aOptErr) + break; + if(do_b == 0) + aOptEndPts.A[ch] = newEndPts.A[ch]; + else + aOptEndPts.B[ch] = newEndPts.B[ch]; + aOptErr = fErr; + do_b = 1 - do_b; // now move the other endpoint + } + } +} + +void D3DX_BC6H::OptimizeEndPoints(const EncodeParams* pEP, const float aOrgErr[], const INTEndPntPair aOrgEndPts[], INTEndPntPair aOptEndPts[]) const +{ + assert( pEP ); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC6H_MAX_REGIONS ); + __analysis_assume( uPartitions < BC6H_MAX_REGIONS ); + INTColor aPixels[NUM_PIXELS_PER_BLOCK]; + + for(size_t p = 0; p <= uPartitions; ++p) + { + // collect the pixels in the region + size_t np = 0; + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + if(g_aPartitionTable[p][pEP->uShape][i] == p) + { + aPixels[np++] = pEP->aIPixels[i]; + } + } + + OptimizeOne(pEP, aPixels, np, aOrgErr[p], aOrgEndPts[p], aOptEndPts[p]); + } +} + +// Swap endpoints as needed to ensure that the indices at fix up have a 0 high-order bit +void D3DX_BC6H::SwapIndices(const EncodeParams* pEP, INTEndPntPair aEndPts[], size_t aIndices[]) +{ + assert( pEP ); + const size_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + const size_t uNumIndices = 1 << ms_aInfo[pEP->uMode].uIndexPrec; + const size_t uHighIndexBit = uNumIndices >> 1; + + assert( uPartitions < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES ); + __analysis_assume( uPartitions < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES ); + + for(size_t p = 0; p <= uPartitions; ++p) + { + size_t i = g_aFixUp[uPartitions][pEP->uShape][p]; + assert(g_aPartitionTable[uPartitions][pEP->uShape][i] == p); + if(aIndices[i] & uHighIndexBit) + { + // high bit is set, swap the aEndPts and indices for this region + Swap(aEndPts[p].A, aEndPts[p].B); + + for(size_t j = 0; j < NUM_PIXELS_PER_BLOCK; ++j) + if(g_aPartitionTable[uPartitions][pEP->uShape][j] == p) + aIndices[j] = uNumIndices - 1 - aIndices[j]; + } + } +} + +// assign indices given a tile, shape, and quantized endpoints, return toterr for each region +void D3DX_BC6H::AssignIndices(const EncodeParams* pEP, const INTEndPntPair aEndPts[], size_t aIndices[], float aTotErr[]) const +{ + assert( pEP ); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + const uint8_t uNumIndices = 1 << ms_aInfo[pEP->uMode].uIndexPrec; + + assert( uPartitions < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES ); + __analysis_assume( uPartitions < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES ); + + // build list of possibles + INTColor aPalette[BC6H_MAX_REGIONS][BC6H_MAX_INDICES]; + + for(size_t p = 0; p <= uPartitions; ++p) + { + GeneratePaletteQuantized(pEP, aEndPts[p], aPalette[p]); + aTotErr[p] = 0; + } + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + const uint8_t uRegion = g_aPartitionTable[uPartitions][pEP->uShape][i]; + assert( uRegion < BC6H_MAX_REGIONS ); + __analysis_assume( uRegion < BC6H_MAX_REGIONS ); + float fBestErr = Norm(pEP->aIPixels[i], aPalette[uRegion][0]); + aIndices[i] = 0; + + for(uint8_t j = 1; j < uNumIndices && fBestErr > 0; ++j) + { + float fErr = Norm(pEP->aIPixels[i], aPalette[uRegion][j]); + if(fErr > fBestErr) break; // error increased, so we're done searching + if(fErr < fBestErr) + { + fBestErr = fErr; + aIndices[i] = j; + } + } + aTotErr[uRegion] += fBestErr; + } +} + +void D3DX_BC6H::QuantizeEndPts(const EncodeParams* pEP, INTEndPntPair* aQntEndPts) const +{ + assert( pEP && aQntEndPts ); + const INTEndPntPair* aUnqEndPts = pEP->aUnqEndPts[pEP->uShape]; + const LDRColorA& Prec = ms_aInfo[pEP->uMode].RGBAPrec[0][0]; + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC6H_MAX_REGIONS ); + __analysis_assume( uPartitions < BC6H_MAX_REGIONS ); + + for(size_t p = 0; p <= uPartitions; ++p) + { + aQntEndPts[p].A.r = Quantize(aUnqEndPts[p].A.r, Prec.r, pEP->bSigned); + aQntEndPts[p].A.g = Quantize(aUnqEndPts[p].A.g, Prec.g, pEP->bSigned); + aQntEndPts[p].A.b = Quantize(aUnqEndPts[p].A.b, Prec.b, pEP->bSigned); + aQntEndPts[p].B.r = Quantize(aUnqEndPts[p].B.r, Prec.r, pEP->bSigned); + aQntEndPts[p].B.g = Quantize(aUnqEndPts[p].B.g, Prec.g, pEP->bSigned); + aQntEndPts[p].B.b = Quantize(aUnqEndPts[p].B.b, Prec.b, pEP->bSigned); + } +} + +void D3DX_BC6H::EmitBlock(const EncodeParams* pEP, const INTEndPntPair aEndPts[], const size_t aIndices[]) +{ + assert( pEP ); + const uint8_t uRealMode = ms_aInfo[pEP->uMode].uMode; + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; + const size_t uHeaderBits = uPartitions > 0 ? 82 : 65; + const ModeDescriptor* desc = ms_aDesc[pEP->uMode]; + size_t uStartBit = 0; + + while(uStartBit < uHeaderBits) + { + switch(desc[uStartBit].m_eField) + { + case M: SetBit(uStartBit, uint8_t(uRealMode >> desc[uStartBit].m_uBit) & 0x01); break; + case D: SetBit(uStartBit, uint8_t(pEP->uShape >> desc[uStartBit].m_uBit) & 0x01); break; + case RW: SetBit(uStartBit, uint8_t(aEndPts[0].A.r >> desc[uStartBit].m_uBit) & 0x01); break; + case RX: SetBit(uStartBit, uint8_t(aEndPts[0].B.r >> desc[uStartBit].m_uBit) & 0x01); break; + case RY: SetBit(uStartBit, uint8_t(aEndPts[1].A.r >> desc[uStartBit].m_uBit) & 0x01); break; + case RZ: SetBit(uStartBit, uint8_t(aEndPts[1].B.r >> desc[uStartBit].m_uBit) & 0x01); break; + case GW: SetBit(uStartBit, uint8_t(aEndPts[0].A.g >> desc[uStartBit].m_uBit) & 0x01); break; + case GX: SetBit(uStartBit, uint8_t(aEndPts[0].B.g >> desc[uStartBit].m_uBit) & 0x01); break; + case GY: SetBit(uStartBit, uint8_t(aEndPts[1].A.g >> desc[uStartBit].m_uBit) & 0x01); break; + case GZ: SetBit(uStartBit, uint8_t(aEndPts[1].B.g >> desc[uStartBit].m_uBit) & 0x01); break; + case BW: SetBit(uStartBit, uint8_t(aEndPts[0].A.b >> desc[uStartBit].m_uBit) & 0x01); break; + case BX: SetBit(uStartBit, uint8_t(aEndPts[0].B.b >> desc[uStartBit].m_uBit) & 0x01); break; + case BY: SetBit(uStartBit, uint8_t(aEndPts[1].A.b >> desc[uStartBit].m_uBit) & 0x01); break; + case BZ: SetBit(uStartBit, uint8_t(aEndPts[1].B.b >> desc[uStartBit].m_uBit) & 0x01); break; + default: assert(false); + } + } + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + if(IsFixUpOffset(ms_aInfo[pEP->uMode].uPartitions, pEP->uShape, i)) + SetBits(uStartBit, uIndexPrec - 1, static_cast( aIndices[i] )); + else + SetBits(uStartBit, uIndexPrec, static_cast( aIndices[i] )); + } + assert(uStartBit == 128); +} + +void D3DX_BC6H::Refine(EncodeParams* pEP) +{ + assert( pEP ); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC6H_MAX_REGIONS ); + __analysis_assume( uPartitions < BC6H_MAX_REGIONS ); + + const bool bTransformed = ms_aInfo[pEP->uMode].bTransformed; + float aOrgErr[BC6H_MAX_REGIONS], aOptErr[BC6H_MAX_REGIONS]; + INTEndPntPair aOrgEndPts[BC6H_MAX_REGIONS], aOptEndPts[BC6H_MAX_REGIONS]; + size_t aOrgIdx[NUM_PIXELS_PER_BLOCK], aOptIdx[NUM_PIXELS_PER_BLOCK]; + + QuantizeEndPts(pEP, aOrgEndPts); + AssignIndices(pEP, aOrgEndPts, aOrgIdx, aOrgErr); + SwapIndices(pEP, aOrgEndPts, aOrgIdx); + + if(bTransformed) TransformForward(aOrgEndPts); + if(EndPointsFit(pEP, aOrgEndPts)) + { + if(bTransformed) TransformInverse(aOrgEndPts, ms_aInfo[pEP->uMode].RGBAPrec[0][0], pEP->bSigned); + OptimizeEndPoints(pEP, aOrgErr, aOrgEndPts, aOptEndPts); + AssignIndices(pEP, aOptEndPts, aOptIdx, aOptErr); + SwapIndices(pEP, aOptEndPts, aOptIdx); + + float fOrgTotErr = 0.0f, fOptTotErr = 0.0f; + for(size_t p = 0; p <= uPartitions; ++p) + { + fOrgTotErr += aOrgErr[p]; + fOptTotErr += aOptErr[p]; + } + + if(bTransformed) TransformForward(aOptEndPts); + if(EndPointsFit(pEP, aOptEndPts) && fOptTotErr < fOrgTotErr && fOptTotErr < pEP->fBestErr) + { + pEP->fBestErr = fOptTotErr; + EmitBlock(pEP, aOptEndPts, aOptIdx); + } + else if(fOrgTotErr < pEP->fBestErr) + { + // either it stopped fitting when we optimized it, or there was no improvement + // so go back to the unoptimized endpoints which we know will fit + if(bTransformed) TransformForward(aOrgEndPts); + pEP->fBestErr = fOrgTotErr; + EmitBlock(pEP, aOrgEndPts, aOrgIdx); + } + } +} + +void D3DX_BC6H::GeneratePaletteUnquantized(const EncodeParams* pEP, size_t uRegion, INTColor aPalette[]) +{ + assert( pEP ); + assert( uRegion < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES ); + __analysis_assume( uRegion < BC6H_MAX_REGIONS && pEP->uShape < BC6H_MAX_SHAPES ); + const INTEndPntPair& endPts = pEP->aUnqEndPts[pEP->uShape][uRegion]; + const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; + const uint8_t uNumIndices = 1 << uIndexPrec; + assert( uNumIndices > 0 ); + __analysis_assume( uNumIndices > 0 ); + + const int* aWeights = nullptr; + switch(uIndexPrec) + { + case 3: aWeights = g_aWeights3; assert(uNumIndices <= 8); __analysis_assume(uNumIndices <= 8); break; + case 4: aWeights = g_aWeights4; assert(uNumIndices <= 16); __analysis_assume(uNumIndices <= 16); break; + default: assert(false); for(size_t i = 0; i < uNumIndices; ++i) aPalette[i] = INTColor(0,0,0); return; + } + + for(register size_t i = 0; i < uNumIndices; ++i) + { + aPalette[i].r = (endPts.A.r * (BC67_WEIGHT_MAX - aWeights[i]) + endPts.B.r * aWeights[i] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT; + aPalette[i].g = (endPts.A.g * (BC67_WEIGHT_MAX - aWeights[i]) + endPts.B.g * aWeights[i] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT; + aPalette[i].b = (endPts.A.b * (BC67_WEIGHT_MAX - aWeights[i]) + endPts.B.b * aWeights[i] + BC67_WEIGHT_ROUND) >> BC67_WEIGHT_SHIFT; + } +} + +float D3DX_BC6H::MapColors(const EncodeParams* pEP, size_t uRegion, size_t np, const size_t* auIndex) const +{ + assert( pEP ); + const uint8_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; + const uint8_t uNumIndices = 1 << uIndexPrec; + INTColor aPalette[BC6H_MAX_INDICES]; + GeneratePaletteUnquantized(pEP, uRegion, aPalette); + + float fTotalErr = 0.0f; + for(size_t i = 0; i < np; ++i) + { + float fBestErr = Norm(pEP->aIPixels[auIndex[i]], aPalette[0]); + for(uint8_t j = 1; j < uNumIndices && fBestErr > 0.0f; ++j) + { + float fErr = Norm(pEP->aIPixels[auIndex[i]], aPalette[j]); + if(fErr > fBestErr) break; // error increased, so we're done searching + if(fErr < fBestErr) fBestErr = fErr; + } + fTotalErr += fBestErr; + } + + return fTotalErr; +} + +float D3DX_BC6H::RoughMSE(EncodeParams* pEP) const +{ + assert( pEP ); + assert( pEP->uShape < BC6H_MAX_SHAPES); + __analysis_assume( pEP->uShape < BC6H_MAX_SHAPES); + + INTEndPntPair* aEndPts = pEP->aUnqEndPts[pEP->uShape]; + + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC6H_MAX_REGIONS ); + __analysis_assume( uPartitions < BC6H_MAX_REGIONS ); + + size_t auPixIdx[NUM_PIXELS_PER_BLOCK]; + + float fError = 0.0f; + for(size_t p = 0; p <= uPartitions; ++p) + { + size_t np = 0; + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + if(g_aPartitionTable[uPartitions][pEP->uShape][i] == p) + { + auPixIdx[np++] = i; + } + } + + // handle simple cases + assert(np > 0); + if(np == 1) + { + aEndPts[p].A = pEP->aIPixels[auPixIdx[0]]; + aEndPts[p].B = pEP->aIPixels[auPixIdx[0]]; + continue; + } + else if(np == 2) + { + aEndPts[p].A = pEP->aIPixels[auPixIdx[0]]; + aEndPts[p].B = pEP->aIPixels[auPixIdx[1]]; + continue; + } + + HDRColorA epA, epB; + OptimizeRGB(pEP->aHDRPixels, &epA, &epB, 4, np, auPixIdx); + aEndPts[p].A.Set(epA, pEP->bSigned); + aEndPts[p].B.Set(epB, pEP->bSigned); + if(pEP->bSigned) + { + aEndPts[p].A.Clamp(-F16MAX, F16MAX); + aEndPts[p].B.Clamp(-F16MAX, F16MAX); + } + else + { + aEndPts[p].A.Clamp(0, F16MAX); + aEndPts[p].B.Clamp(0, F16MAX); + } + + fError += MapColors(pEP, p, np, auPixIdx); + } + + return fError; +} + + + +//------------------------------------------------------------------------------------- +// BC7 Compression +//------------------------------------------------------------------------------------- +void D3DX_BC7::Decode(HDRColorA* pOut) const +{ + assert( pOut ); + + size_t uFirst = 0; + while(uFirst < 128 && !GetBit(uFirst)) {} + uint8_t uMode = uint8_t(uFirst - 1); + + if(uMode < 8) + { + const uint8_t uPartitions = ms_aInfo[uMode].uPartitions; + assert( uPartitions < BC7_MAX_REGIONS ); + __analysis_assume( uPartitions < BC7_MAX_REGIONS ); + + const uint8_t uNumEndPts = (uPartitions + 1) << 1; + const uint8_t uIndexPrec = ms_aInfo[uMode].uIndexPrec; + const uint8_t uIndexPrec2 = ms_aInfo[uMode].uIndexPrec2; + register size_t i; + size_t uStartBit = uMode + 1; + uint8_t P[6]; + uint8_t uShape = GetBits(uStartBit, ms_aInfo[uMode].uPartitionBits); + assert( uShape < BC7_MAX_SHAPES ); + __analysis_assume( uShape < BC7_MAX_SHAPES ); + + uint8_t uRotation = GetBits(uStartBit, ms_aInfo[uMode].uRotationBits); + assert( uRotation < 4 ); + + uint8_t uIndexMode = GetBits(uStartBit, ms_aInfo[uMode].uIndexModeBits); + assert( uIndexMode < 2 ); + + LDRColorA c[BC7_MAX_REGIONS << 1]; + const LDRColorA RGBAPrec = ms_aInfo[uMode].RGBAPrec; + const LDRColorA RGBAPrecWithP = ms_aInfo[uMode].RGBAPrecWithP; + + assert( uNumEndPts <= (BC7_MAX_REGIONS << 1) ); + + // Red channel + for(i = 0; i < uNumEndPts; i++) + { + if ( uStartBit + RGBAPrec.r > 128 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + + c[i].r = GetBits(uStartBit, RGBAPrec.r); + } + + // Green channel + for(i = 0; i < uNumEndPts; i++) + { + if ( uStartBit + RGBAPrec.g > 128 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + + c[i].g = GetBits(uStartBit, RGBAPrec.g); + } + + // Blue channel + for(i = 0; i < uNumEndPts; i++) + { + if ( uStartBit + RGBAPrec.b > 128 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + + c[i].b = GetBits(uStartBit, RGBAPrec.b); + } + + // Alpha channel + for(i = 0; i < uNumEndPts; i++) + { + if ( uStartBit + RGBAPrec.a > 128 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + + c[i].a = RGBAPrec.a ? GetBits(uStartBit, RGBAPrec.a) : 255; + } + + // P-bits + assert( ms_aInfo[uMode].uPBits <= 6 ); + __analysis_assume( ms_aInfo[uMode].uPBits <= 6 ); + for(i = 0; i < ms_aInfo[uMode].uPBits; i++) + { + if ( uStartBit > 127 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + + P[i] = GetBit(uStartBit); + } + + if(ms_aInfo[uMode].uPBits) + { + for(i = 0; i < uNumEndPts; i++) + { + size_t pi = i * ms_aInfo[uMode].uPBits / uNumEndPts; + for(register uint8_t ch = 0; ch < BC7_NUM_CHANNELS; ch++) + { + if(RGBAPrec[ch] != RGBAPrecWithP[ch]) + { + c[i][ch] = (c[i][ch] << 1) | P[pi]; + } + } + } + } + + for(i = 0; i < uNumEndPts; i++) + { + c[i] = Unquantize(c[i], RGBAPrecWithP); + } + + uint8_t w1[NUM_PIXELS_PER_BLOCK], w2[NUM_PIXELS_PER_BLOCK]; + + // read color indices + for(i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + { + size_t uNumBits = IsFixUpOffset(ms_aInfo[uMode].uPartitions, uShape, i) ? uIndexPrec - 1 : uIndexPrec; + if ( uStartBit + uNumBits > 128 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + w1[i] = GetBits(uStartBit, uNumBits); + } + + // read alpha indices + if(uIndexPrec2) + { + for(i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + { + size_t uNumBits = i ? uIndexPrec2 : uIndexPrec2 - 1; + if ( uStartBit + uNumBits > 128 ) + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid block encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + return; + } + w2[i] = GetBits(uStartBit, uNumBits ); + } + } + + for(i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + uint8_t uRegion = g_aPartitionTable[uPartitions][uShape][i]; + LDRColorA outPixel; + if(uIndexPrec2 == 0) + { + LDRColorA::Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w1[i], uIndexPrec, uIndexPrec, outPixel); + } + else + { + if(uIndexMode == 0) + { + LDRColorA::Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w2[i], uIndexPrec, uIndexPrec2, outPixel); + } + else + { + LDRColorA::Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w2[i], w1[i], uIndexPrec2, uIndexPrec, outPixel); + } + } + + switch(uRotation) + { + case 1: Swap(outPixel.r, outPixel.a); break; + case 2: Swap(outPixel.g, outPixel.a); break; + case 3: Swap(outPixel.b, outPixel.a); break; + } + + pOut[i] = HDRColorA(outPixel); + } + } + else + { +#ifdef DEBUG + OutputDebugStringA( "BC7: Invalid mode encountered during decoding\n" ); +#endif + FillWithErrorColors( pOut ); + } +} + +void D3DX_BC7::Encode(const HDRColorA* const pIn) +{ + assert( pIn ); + + D3DX_BC7 final = *this; + EncodeParams EP(pIn); + float fMSEBest = FLT_MAX; + + for(size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + EP.aLDRPixels[i].r = uint8_t( std::max( 0.0f, std::min( 255.0f, pIn[i].r * 255.0f + 0.01f ) ) ); + EP.aLDRPixels[i].g = uint8_t( std::max( 0.0f, std::min( 255.0f, pIn[i].g * 255.0f + 0.01f ) ) ); + EP.aLDRPixels[i].b = uint8_t( std::max( 0.0f, std::min( 255.0f, pIn[i].b * 255.0f + 0.01f ) ) ); + EP.aLDRPixels[i].a = uint8_t( std::max( 0.0f, std::min( 255.0f, pIn[i].a * 255.0f + 0.01f ) ) ); + } + + for(EP.uMode = 0; EP.uMode < 8 && fMSEBest > 0; ++EP.uMode) + { + const size_t uShapes = 1 << ms_aInfo[EP.uMode].uPartitionBits; + const size_t uNumRots = 1 << ms_aInfo[EP.uMode].uRotationBits; + const size_t uNumIdxMode = 1 << ms_aInfo[EP.uMode].uIndexModeBits; + // Number of rough cases to look at. reasonable values of this are 1, uShapes/4, and uShapes + // uShapes/4 gets nearly all the cases; you can increase that a bit (say by 3 or 4) if you really want to squeeze the last bit out + const size_t uItems = std::max(1, uShapes >> 2); + float afRoughMSE[BC7_MAX_SHAPES]; + size_t auShape[BC7_MAX_SHAPES]; + + for(size_t r = 0; r < uNumRots && fMSEBest > 0; ++r) + { + switch(r) + { + case 1: for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) Swap(EP.aLDRPixels[i].r, EP.aLDRPixels[i].a); break; + case 2: for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) Swap(EP.aLDRPixels[i].g, EP.aLDRPixels[i].a); break; + case 3: for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) Swap(EP.aLDRPixels[i].b, EP.aLDRPixels[i].a); break; + } + + for(size_t im = 0; im < uNumIdxMode && fMSEBest > 0; ++im) + { + // pick the best uItems shapes and refine these. + for(size_t s = 0; s < uShapes; s++) + { + afRoughMSE[s] = RoughMSE(&EP, s, im); + auShape[s] = s; + } + + // Bubble up the first uItems items + for(size_t i = 0; i < uItems; i++) + { + for(size_t j = i + 1; j < uShapes; j++) + { + if(afRoughMSE[i] > afRoughMSE[j]) + { + Swap(afRoughMSE[i], afRoughMSE[j]); + Swap(auShape[i], auShape[j]); + } + } + } + + for(size_t i = 0; i < uItems && fMSEBest > 0; i++) + { + float fMSE = Refine(&EP, auShape[i], r, im); + if(fMSE < fMSEBest) + { + final = *this; + fMSEBest = fMSE; + } + } + } + + switch(r) + { + case 1: for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) Swap(EP.aLDRPixels[i].r, EP.aLDRPixels[i].a); break; + case 2: for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) Swap(EP.aLDRPixels[i].g, EP.aLDRPixels[i].a); break; + case 3: for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) Swap(EP.aLDRPixels[i].b, EP.aLDRPixels[i].a); break; + } + } + } + + *this = final; +} + + +//------------------------------------------------------------------------------------- +void D3DX_BC7::GeneratePaletteQuantized(const EncodeParams* pEP, size_t uIndexMode, const LDREndPntPair& endPts, LDRColorA aPalette[]) const +{ + assert( pEP ); + const size_t uIndexPrec = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec2 : ms_aInfo[pEP->uMode].uIndexPrec; + const size_t uIndexPrec2 = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec : ms_aInfo[pEP->uMode].uIndexPrec2; + const size_t uNumIndices = 1 << uIndexPrec; + const size_t uNumIndices2 = 1 << uIndexPrec2; + assert( uNumIndices > 0 && uNumIndices2 > 0 ); + __analysis_assume( uNumIndices > 0 && uNumIndices2 > 0 ); + assert( (uNumIndices <= BC7_MAX_INDICES) && (uNumIndices2 <= BC7_MAX_INDICES) ); + __analysis_assume( (uNumIndices <= BC7_MAX_INDICES) && (uNumIndices2 <= BC7_MAX_INDICES) ); + + LDRColorA a = Unquantize(endPts.A, ms_aInfo[pEP->uMode].RGBAPrecWithP); + LDRColorA b = Unquantize(endPts.B, ms_aInfo[pEP->uMode].RGBAPrecWithP); + if(uIndexPrec2 == 0) + { + for(register size_t i = 0; i < uNumIndices; i++) + LDRColorA::Interpolate(a, b, i, i, uIndexPrec, uIndexPrec, aPalette[i]); + } + else + { + for(register size_t i = 0; i < uNumIndices; i++) + LDRColorA::InterpolateRGB(a, b, i, uIndexPrec, aPalette[i]); + for(register size_t i = 0; i < uNumIndices2; i++) + LDRColorA::InterpolateA(a, b, i, uIndexPrec2, aPalette[i]); + } +} + +float D3DX_BC7::PerturbOne(const EncodeParams* pEP, const LDRColorA aColors[], size_t np, size_t uIndexMode, size_t ch, + const LDREndPntPair &oldEndPts, LDREndPntPair &newEndPts, float fOldErr, uint8_t do_b) const +{ + assert( pEP ); + const int prec = ms_aInfo[pEP->uMode].RGBAPrecWithP[ch]; + LDREndPntPair tmp_endPts = newEndPts = oldEndPts; + float fMinErr = fOldErr; + uint8_t* pnew_c = (do_b ? &newEndPts.B[ch] : &newEndPts.A[ch]); + uint8_t* ptmp_c = (do_b ? &tmp_endPts.B[ch] : &tmp_endPts.A[ch]); + + // do a logarithmic search for the best error for this endpoint (which) + for(int step = 1 << (prec-1); step; step >>= 1) + { + bool bImproved = false; + int beststep = 0; + for(int sign = -1; sign <= 1; sign += 2) + { + int tmp = int(*pnew_c) + sign * step; + if(tmp < 0 || tmp >= (1 << prec)) + continue; + else + *ptmp_c = (uint8_t) tmp; + + float fTotalErr = MapColors(pEP, aColors, np, uIndexMode, tmp_endPts, fMinErr); + if(fTotalErr < fMinErr) + { + bImproved = true; + fMinErr = fTotalErr; + beststep = sign * step; + } + } + + // if this was an improvement, move the endpoint and continue search from there + if(bImproved) + *pnew_c = uint8_t(int(*pnew_c) + beststep); + } + return fMinErr; +} + +// perturb the endpoints at least -3 to 3. +// always ensure endpoint ordering is preserved (no need to overlap the scan) +void D3DX_BC7::Exhaustive(const EncodeParams* pEP, const LDRColorA aColors[], size_t np, size_t uIndexMode, size_t ch, + float& fOrgErr, LDREndPntPair& optEndPt) const +{ + assert( pEP ); + const uint8_t uPrec = ms_aInfo[pEP->uMode].RGBAPrecWithP[ch]; + LDREndPntPair tmpEndPt; + if(fOrgErr == 0) + return; + + int delta = 5; + + // ok figure out the range of A and B + tmpEndPt = optEndPt; + int alow = std::max(0, int(optEndPt.A[ch]) - delta); + int ahigh = std::min((1 << uPrec) - 1, int(optEndPt.A[ch]) + delta); + int blow = std::max(0, int(optEndPt.B[ch]) - delta); + int bhigh = std::min((1 << uPrec) - 1, int(optEndPt.B[ch]) + delta); + int amin = 0; + int bmin = 0; + + float fBestErr = fOrgErr; + if(optEndPt.A[ch] <= optEndPt.B[ch]) + { + // keep a <= b + for(int a = alow; a <= ahigh; ++a) + { + for(int b = std::max(a, blow); b < bhigh; ++b) + { + tmpEndPt.A[ch] = (uint8_t) a; + tmpEndPt.B[ch] = (uint8_t) b; + + float fErr = MapColors(pEP, aColors, np, uIndexMode, tmpEndPt, fBestErr); + if(fErr < fBestErr) + { + amin = a; + bmin = b; + fBestErr = fErr; + } + } + } + } + else + { + // keep b <= a + for(int b = blow; b < bhigh; ++b) + { + for(int a = std::max(b, alow); a <= ahigh; ++a) + { + tmpEndPt.A[ch] = (uint8_t) a; + tmpEndPt.B[ch] = (uint8_t) b; + + float fErr = MapColors(pEP, aColors, np, uIndexMode, tmpEndPt, fBestErr); + if(fErr < fBestErr) + { + amin = a; + bmin = b; + fBestErr = fErr; + } + } + } + } + + if(fBestErr < fOrgErr) + { + optEndPt.A[ch] = (uint8_t) amin; + optEndPt.B[ch] = (uint8_t) bmin; + fOrgErr = fBestErr; + } +} + +void D3DX_BC7::OptimizeOne(const EncodeParams* pEP, const LDRColorA aColors[], size_t np, size_t uIndexMode, + float fOrgErr, const LDREndPntPair& org, LDREndPntPair& opt) const +{ + assert( pEP ); + + float fOptErr = fOrgErr; + opt = org; + + LDREndPntPair new_a, new_b; + LDREndPntPair newEndPts; + uint8_t do_b; + + // now optimize each channel separately + for(size_t ch = 0; ch < BC7_NUM_CHANNELS; ++ch) + { + if(ms_aInfo[pEP->uMode].RGBAPrecWithP[ch] == 0) + continue; + + // figure out which endpoint when perturbed gives the most improvement and start there + // if we just alternate, we can easily end up in a local minima + float fErr0 = PerturbOne(pEP, aColors, np, uIndexMode, ch, opt, new_a, fOptErr, 0); // perturb endpt A + float fErr1 = PerturbOne(pEP, aColors, np, uIndexMode, ch, opt, new_b, fOptErr, 1); // perturb endpt B + + uint8_t& copt_a = opt.A[ch]; + uint8_t& copt_b = opt.B[ch]; + uint8_t& cnew_a = new_a.A[ch]; + uint8_t& cnew_b = new_a.B[ch]; + + if(fErr0 < fErr1) + { + if(fErr0 >= fOptErr) + continue; + copt_a = cnew_a; + fOptErr = fErr0; + do_b = 1; // do B next + } + else + { + if(fErr1 >= fOptErr) + continue; + copt_b = cnew_b; + fOptErr = fErr1; + do_b = 0; // do A next + } + + // now alternate endpoints and keep trying until there is no improvement + for( ; ; ) + { + float fErr = PerturbOne(pEP, aColors, np, uIndexMode, ch, opt, newEndPts, fOptErr, do_b); + if(fErr >= fOptErr) + break; + if(do_b == 0) + copt_a = cnew_a; + else + copt_b = cnew_b; + fOptErr = fErr; + do_b = 1 - do_b; // now move the other endpoint + } + } + + // finally, do a small exhaustive search around what we think is the global minima to be sure + for(size_t ch = 0; ch < BC7_NUM_CHANNELS; ch++) + Exhaustive(pEP, aColors, np, uIndexMode, ch, fOptErr, opt); +} + +void D3DX_BC7::OptimizeEndPoints(const EncodeParams* pEP, size_t uShape, size_t uIndexMode, const float afOrgErr[], + const LDREndPntPair aOrgEndPts[], LDREndPntPair aOptEndPts[]) const +{ + assert( pEP ); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC7_MAX_REGIONS && uShape < BC7_MAX_SHAPES ); + __analysis_assume( uPartitions < BC7_MAX_REGIONS && uShape < BC7_MAX_SHAPES ); + + LDRColorA aPixels[NUM_PIXELS_PER_BLOCK]; + + for(size_t p = 0; p <= uPartitions; ++p) + { + // collect the pixels in the region + size_t np = 0; + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + if(g_aPartitionTable[uPartitions][uShape][i] == p) + aPixels[np++] = pEP->aLDRPixels[i]; + + OptimizeOne(pEP, aPixels, np, uIndexMode, afOrgErr[p], aOrgEndPts[p], aOptEndPts[p]); + } +} + +void D3DX_BC7::AssignIndices(const EncodeParams* pEP, size_t uShape, size_t uIndexMode, LDREndPntPair endPts[], size_t aIndices[], size_t aIndices2[], + float afTotErr[]) const +{ + assert( pEP ); + assert( uShape < BC7_MAX_SHAPES ); + __analysis_assume( uShape < BC7_MAX_SHAPES ); + + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC7_MAX_REGIONS ); + __analysis_assume( uPartitions < BC7_MAX_REGIONS ); + + const uint8_t uIndexPrec = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec2 : ms_aInfo[pEP->uMode].uIndexPrec; + const uint8_t uIndexPrec2 = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec : ms_aInfo[pEP->uMode].uIndexPrec2; + const uint8_t uNumIndices = 1 << uIndexPrec; + const uint8_t uNumIndices2 = 1 << uIndexPrec2; + + assert( (uNumIndices <= BC7_MAX_INDICES) && (uNumIndices2 <= BC7_MAX_INDICES) ); + __analysis_assume( (uNumIndices <= BC7_MAX_INDICES) && (uNumIndices2 <= BC7_MAX_INDICES) ); + + const uint8_t uHighestIndexBit = uNumIndices >> 1; + const uint8_t uHighestIndexBit2 = uNumIndices2 >> 1; + LDRColorA aPalette[BC7_MAX_REGIONS][BC7_MAX_INDICES]; + + // build list of possibles + LDREndPntPair adjusted_endPts; + for(size_t p = 0; p <= uPartitions; p++) + { + GeneratePaletteQuantized(pEP, uIndexMode, endPts[p], aPalette[p]); + afTotErr[p] = 0; + } + + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + { + uint8_t uRegion = g_aPartitionTable[uPartitions][uShape][i]; + assert( uRegion < BC7_MAX_REGIONS ); + __analysis_assume( uRegion < BC7_MAX_REGIONS ); + afTotErr[uRegion] += ComputeError(pEP->aLDRPixels[i], aPalette[uRegion], uIndexPrec, uIndexPrec2, &(aIndices[i]), &(aIndices2[i])); + } + + // swap endpoints as needed to ensure that the indices at index_positions have a 0 high-order bit + if(uIndexPrec2 == 0) + { + for(register size_t p = 0; p <= uPartitions; p++) + { + if(aIndices[g_aFixUp[uPartitions][uShape][p]] & uHighestIndexBit) + { + Swap(endPts[p].A, endPts[p].B); + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + if(g_aPartitionTable[uPartitions][uShape][i] == p) + aIndices[i] = uNumIndices - 1 - aIndices[i]; + } + assert((aIndices[g_aFixUp[uPartitions][uShape][p]] & uHighestIndexBit) == 0); + } + } + else + { + for(register size_t p = 0; p <= uPartitions; p++) + { + if(aIndices[g_aFixUp[uPartitions][uShape][p]] & uHighestIndexBit) + { + Swap(endPts[p].A.r, endPts[p].B.r); + Swap(endPts[p].A.g, endPts[p].B.g); + Swap(endPts[p].A.b, endPts[p].B.b); + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + if(g_aPartitionTable[uPartitions][uShape][i] == p) + aIndices[i] = uNumIndices - 1 - aIndices[i]; + } + assert((aIndices[g_aFixUp[uPartitions][uShape][p]] & uHighestIndexBit) == 0); + + if(aIndices2[0] & uHighestIndexBit2) + { + Swap(endPts[p].A.a, endPts[p].B.a); + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + aIndices2[i] = uNumIndices2 - 1 - aIndices2[i]; + } + assert((aIndices2[0] & uHighestIndexBit2) == 0); + } + } +} + +void D3DX_BC7::EmitBlock(const EncodeParams* pEP, size_t uShape, size_t uRotation, size_t uIndexMode, const LDREndPntPair aEndPts[], const size_t aIndex[], const size_t aIndex2[]) +{ + assert( pEP ); + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC7_MAX_REGIONS ); + __analysis_assume( uPartitions < BC7_MAX_REGIONS ); + + const size_t uPBits = ms_aInfo[pEP->uMode].uPBits; + const size_t uIndexPrec = ms_aInfo[pEP->uMode].uIndexPrec; + const size_t uIndexPrec2 = ms_aInfo[pEP->uMode].uIndexPrec2; + const LDRColorA RGBAPrec = ms_aInfo[pEP->uMode].RGBAPrec; + const LDRColorA RGBAPrecWithP = ms_aInfo[pEP->uMode].RGBAPrecWithP; + register size_t i; + size_t uStartBit = 0; + SetBits(uStartBit, pEP->uMode, 0); + SetBits(uStartBit, 1, 1); + SetBits(uStartBit, ms_aInfo[pEP->uMode].uRotationBits, static_cast( uRotation )); + SetBits(uStartBit, ms_aInfo[pEP->uMode].uIndexModeBits, static_cast( uIndexMode )); + SetBits(uStartBit, ms_aInfo[pEP->uMode].uPartitionBits, static_cast( uShape )); + + if(uPBits) + { + const size_t uNumEP = (1 + uPartitions) << 1; + uint8_t aPVote[BC7_MAX_REGIONS << 1] = {0,0,0,0,0,0}; + uint8_t aCount[BC7_MAX_REGIONS << 1] = {0,0,0,0,0,0}; + for(uint8_t ch = 0; ch < BC7_NUM_CHANNELS; ch++) + { + uint8_t ep = 0; + for(i = 0; i <= uPartitions; i++) + { + if(RGBAPrec[ch] == RGBAPrecWithP[ch]) + { + SetBits(uStartBit, RGBAPrec[ch], aEndPts[i].A[ch]); + SetBits(uStartBit, RGBAPrec[ch], aEndPts[i].B[ch]); + } + else + { + SetBits(uStartBit, RGBAPrec[ch], aEndPts[i].A[ch] >> 1); + SetBits(uStartBit, RGBAPrec[ch], aEndPts[i].B[ch] >> 1); + size_t idx = ep++ * uPBits / uNumEP; + assert(idx < (BC7_MAX_REGIONS << 1)); + __analysis_assume(idx < (BC7_MAX_REGIONS << 1)); + aPVote[idx] += aEndPts[i].A[ch] & 0x01; + aCount[idx]++; + idx = ep++ * uPBits / uNumEP; + assert(idx < (BC7_MAX_REGIONS << 1)); + __analysis_assume(idx < (BC7_MAX_REGIONS << 1)); + aPVote[idx] += aEndPts[i].B[ch] & 0x01; + aCount[idx]++; + } + } + } + + for(i = 0; i < uPBits; i++) + { + SetBits(uStartBit, 1, aPVote[i] > (aCount[i] >> 1) ? 1 : 0); + } + } + else + { + for(size_t ch = 0; ch < BC7_NUM_CHANNELS; ch++) + { + for(i = 0; i <= uPartitions; i++) + { + SetBits(uStartBit, RGBAPrec[ch], aEndPts[i].A[ch] ); + SetBits(uStartBit, RGBAPrec[ch], aEndPts[i].B[ch] ); + } + } + } + + const size_t* aI1 = uIndexMode ? aIndex2 : aIndex; + const size_t* aI2 = uIndexMode ? aIndex : aIndex2; + for(i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + { + if(IsFixUpOffset(ms_aInfo[pEP->uMode].uPartitions, uShape, i)) + SetBits(uStartBit, uIndexPrec - 1, static_cast( aI1[i] )); + else + SetBits(uStartBit, uIndexPrec, static_cast( aI1[i] )); + } + if(uIndexPrec2) + for(i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + SetBits(uStartBit, i ? uIndexPrec2 : uIndexPrec2 - 1, static_cast( aI2[i] )); + + assert(uStartBit == 128); +} + +float D3DX_BC7::Refine(const EncodeParams* pEP, size_t uShape, size_t uRotation, size_t uIndexMode) +{ + assert( pEP ); + assert( uShape < BC7_MAX_SHAPES ); + __analysis_assume( uShape < BC7_MAX_SHAPES ); + const LDREndPntPair* aEndPts = pEP->aEndPts[uShape]; + + const size_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC7_MAX_REGIONS ); + __analysis_assume( uPartitions < BC7_MAX_REGIONS ); + + LDREndPntPair aOrgEndPts[BC7_MAX_REGIONS]; + LDREndPntPair aOptEndPts[BC7_MAX_REGIONS]; + size_t aOrgIdx[NUM_PIXELS_PER_BLOCK]; + size_t aOrgIdx2[NUM_PIXELS_PER_BLOCK]; + size_t aOptIdx[NUM_PIXELS_PER_BLOCK]; + size_t aOptIdx2[NUM_PIXELS_PER_BLOCK]; + float aOrgErr[BC7_MAX_REGIONS]; + float aOptErr[BC7_MAX_REGIONS]; + + for(register size_t p = 0; p <= uPartitions; p++) + { + aOrgEndPts[p].A = Quantize(aEndPts[p].A, ms_aInfo[pEP->uMode].RGBAPrecWithP); + aOrgEndPts[p].B = Quantize(aEndPts[p].B, ms_aInfo[pEP->uMode].RGBAPrecWithP); + } + + AssignIndices(pEP, uShape, uIndexMode, aOrgEndPts, aOrgIdx, aOrgIdx2, aOrgErr); + OptimizeEndPoints(pEP, uShape, uIndexMode, aOrgErr, aOrgEndPts, aOptEndPts); + AssignIndices(pEP, uShape, uIndexMode, aOptEndPts, aOptIdx, aOptIdx2, aOptErr); + + float fOrgTotErr = 0, fOptTotErr = 0; + for(register size_t p = 0; p <= uPartitions; p++) + { + fOrgTotErr += aOrgErr[p]; + fOptTotErr += aOptErr[p]; + } + if(fOptTotErr < fOrgTotErr) + { + EmitBlock(pEP, uShape, uRotation, uIndexMode, aOptEndPts, aOptIdx, aOptIdx2); + return fOptTotErr; + } + else + { + EmitBlock(pEP, uShape, uRotation, uIndexMode, aOrgEndPts, aOrgIdx, aOrgIdx2); + return fOrgTotErr; + } +} + +float D3DX_BC7::MapColors(const EncodeParams* pEP, const LDRColorA aColors[], size_t np, size_t uIndexMode, const LDREndPntPair& endPts, float fMinErr) const +{ + assert( pEP ); + const uint8_t uIndexPrec = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec2 : ms_aInfo[pEP->uMode].uIndexPrec; + const uint8_t uIndexPrec2 = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec : ms_aInfo[pEP->uMode].uIndexPrec2; + LDRColorA aPalette[BC7_MAX_INDICES]; + float fTotalErr = 0; + + GeneratePaletteQuantized(pEP, uIndexMode, endPts, aPalette); + for(register size_t i = 0; i < np; ++i) + { + fTotalErr += ComputeError(aColors[i], aPalette, uIndexPrec, uIndexPrec2); + if(fTotalErr > fMinErr) // check for early exit + { + fTotalErr = FLT_MAX; + break; + } + } + + return fTotalErr; +} + +float D3DX_BC7::RoughMSE(EncodeParams* pEP, size_t uShape, size_t uIndexMode) +{ + assert( pEP ); + assert( uShape < BC7_MAX_SHAPES ); + __analysis_assume( uShape < BC7_MAX_SHAPES ); + LDREndPntPair* aEndPts = pEP->aEndPts[uShape]; + + const uint8_t uPartitions = ms_aInfo[pEP->uMode].uPartitions; + assert( uPartitions < BC7_MAX_REGIONS ); + __analysis_assume( uPartitions < BC7_MAX_REGIONS ); + + const uint8_t uIndexPrec = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec2 : ms_aInfo[pEP->uMode].uIndexPrec; + const uint8_t uIndexPrec2 = uIndexMode ? ms_aInfo[pEP->uMode].uIndexPrec : ms_aInfo[pEP->uMode].uIndexPrec2; + const uint8_t uNumIndices = 1 << uIndexPrec; + const uint8_t uNumIndices2 = 1 << uIndexPrec2; + size_t auPixIdx[NUM_PIXELS_PER_BLOCK]; + LDRColorA aPalette[BC7_MAX_REGIONS][BC7_MAX_INDICES]; + + for(size_t p = 0; p <= uPartitions; p++) + { + size_t np = 0; + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + { + if (g_aPartitionTable[uPartitions][uShape][i] == p) + { + auPixIdx[np++] = i; + } + } + + // handle simple cases + assert(np > 0); + if(np == 1) + { + aEndPts[p].A = pEP->aLDRPixels[auPixIdx[0]]; + aEndPts[p].B = pEP->aLDRPixels[auPixIdx[0]]; + continue; + } + else if(np == 2) + { + aEndPts[p].A = pEP->aLDRPixels[auPixIdx[0]]; + aEndPts[p].B = pEP->aLDRPixels[auPixIdx[1]]; + continue; + } + + if(uIndexPrec2 == 0) + { + HDRColorA epA, epB; + OptimizeRGBA(pEP->aHDRPixels, &epA, &epB, 4, np, auPixIdx); + epA.Clamp(0.0f, 1.0f); + epB.Clamp(0.0f, 1.0f); + epA *= 255.0f; + epB *= 255.0f; + aEndPts[p].A = epA.ToLDRColorA(); + aEndPts[p].B = epB.ToLDRColorA(); + } + else + { + uint8_t uMinAlpha = 255, uMaxAlpha = 0; + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i) + { + uMinAlpha = std::min(uMinAlpha, pEP->aLDRPixels[auPixIdx[i]].a); + uMaxAlpha = std::max(uMaxAlpha, pEP->aLDRPixels[auPixIdx[i]].a); + } + + HDRColorA epA, epB; + OptimizeRGB(pEP->aHDRPixels, &epA, &epB, 4, np, auPixIdx); + epA.Clamp(0.0f, 1.0f); + epB.Clamp(0.0f, 1.0f); + epA *= 255.0f; + epB *= 255.0f; + aEndPts[p].A = epA.ToLDRColorA(); + aEndPts[p].B = epB.ToLDRColorA(); + aEndPts[p].A.a = uMinAlpha; + aEndPts[p].B.a = uMaxAlpha; + } + } + + if(uIndexPrec2 == 0) + { + for(size_t p = 0; p <= uPartitions; p++) + for(register size_t i = 0; i < uNumIndices; i++) + LDRColorA::Interpolate(aEndPts[p].A, aEndPts[p].B, i, i, uIndexPrec, uIndexPrec, aPalette[p][i]); + } + else + { + for(size_t p = 0; p <= uPartitions; p++) + { + for(register size_t i = 0; i < uNumIndices; i++) + LDRColorA::InterpolateRGB(aEndPts[p].A, aEndPts[p].B, i, uIndexPrec, aPalette[p][i]); + for(register size_t i = 0; i < uNumIndices2; i++) + LDRColorA::InterpolateA(aEndPts[p].A, aEndPts[p].B, i, uIndexPrec2, aPalette[p][i]); + } + } + + float fTotalErr = 0; + for(register size_t i = 0; i < NUM_PIXELS_PER_BLOCK; i++) + { + uint8_t uRegion = g_aPartitionTable[uPartitions][uShape][i]; + fTotalErr += ComputeError(pEP->aLDRPixels[i], aPalette[uRegion], uIndexPrec, uIndexPrec2); + } + + return fTotalErr; +} + +//===================================================================================== +// Entry points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// BC6H Compression +//------------------------------------------------------------------------------------- +void D3DXDecodeBC6HU(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(D3DX_BC6H) == 16, "D3DX_BC6H should be 16 bytes" ); + reinterpret_cast< const D3DX_BC6H* >( pBC )->Decode(false, reinterpret_cast(pColor)); +} + +void D3DXDecodeBC6HS(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(D3DX_BC6H) == 16, "D3DX_BC6H should be 16 bytes" ); + reinterpret_cast< const D3DX_BC6H* >( pBC )->Decode(true, reinterpret_cast(pColor)); +} + +void D3DXEncodeBC6HU(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags) +{ + UNREFERENCED_PARAMETER(flags); + assert( pBC && pColor ); + static_assert( sizeof(D3DX_BC6H) == 16, "D3DX_BC6H should be 16 bytes" ); + reinterpret_cast< D3DX_BC6H* >( pBC )->Encode(false, reinterpret_cast(pColor)); +} + +void D3DXEncodeBC6HS(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags) +{ + UNREFERENCED_PARAMETER(flags); + assert( pBC && pColor ); + static_assert( sizeof(D3DX_BC6H) == 16, "D3DX_BC6H should be 16 bytes" ); + reinterpret_cast< D3DX_BC6H* >( pBC )->Encode(true, reinterpret_cast(pColor)); +} + + +//------------------------------------------------------------------------------------- +// BC7 Compression +//------------------------------------------------------------------------------------- +void D3DXDecodeBC7(XMVECTOR *pColor, const uint8_t *pBC) +{ + assert( pColor && pBC ); + static_assert( sizeof(D3DX_BC7) == 16, "D3DX_BC7 should be 16 bytes" ); + reinterpret_cast< const D3DX_BC7* >( pBC )->Decode(reinterpret_cast(pColor)); +} + +void D3DXEncodeBC7(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags) +{ + UNREFERENCED_PARAMETER(flags); + assert( pBC && pColor ); + static_assert( sizeof(D3DX_BC7) == 16, "D3DX_BC7 should be 16 bytes" ); + reinterpret_cast< D3DX_BC7* >( pBC )->Encode(reinterpret_cast(pColor)); +} + +} // namespace \ No newline at end of file diff --git a/DirectXTex/DDS.h b/DirectXTex/DDS.h new file mode 100644 index 0000000..142e79e --- /dev/null +++ b/DirectXTex/DDS.h @@ -0,0 +1,214 @@ +//-------------------------------------------------------------------------------------- +// dds.h +// +// This header defines constants and structures that are useful when parsing +// DDS files. DDS files were originally designed to use several structures +// and constants that are native to DirectDraw and are defined in ddraw.h, +// such as DDSURFACEDESC2 and DDSCAPS2. This file defines similar +// (compatible) constants and structures so that one can use DDS files +// without needing to include ddraw.h. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//-------------------------------------------------------------------------------------- + +#if defined(_MSC_VER) && (_MSC_VER > 1000) +#pragma once +#endif + +#include + +#pragma warning(push) +#pragma warning(disable : 4005) +#include +#pragma warning(pop) + +namespace DirectX +{ + +#pragma pack(push,1) + +const uint32_t DDS_MAGIC = 0x20534444; // "DDS " + +struct DDS_PIXELFORMAT +{ + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwFourCC; + uint32_t dwRGBBitCount; + uint32_t dwRBitMask; + uint32_t dwGBitMask; + uint32_t dwBBitMask; + uint32_t dwABitMask; +}; + +#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_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 + +#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) */ + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT1 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','1'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT2 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','2'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT3 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','3'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT4 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','4'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DXT5 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','T','5'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC4_UNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','U'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC4_SNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','4','S'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC5_UNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','U'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_BC5_SNORM = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B','C','5','S'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R8G8_B8G8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R','G','B','G'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_G8R8_G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G','R','G','B'), 0, 0, 0, 0, 0 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_X8R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_X8B8G8R8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_G16R16 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R5G6B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A1R5G5B5 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A4R4G4B4 = + { sizeof(DDS_PIXELFORMAT), DDS_RGBA, 0, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_R8G8B8 = + { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0xff, 0x00, 0x00, 0x00 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_L16 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 16, 0xffff, 0x0000, 0x0000, 0x0000 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8L8 = + { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCEA, 0, 16, 0x00ff, 0x0000, 0x0000, 0xff00 }; + +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_A8 = + { sizeof(DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0x00, 0x00, 0x00, 0xff }; + +// D3DFMT_A2R10G10B10/D3DFMT_A2B10G10R10 should be written using DX10 extension to avoid D3DX 10:10:10:2 reversal issue + +// This indicates the DDS_HEADER_DXT10 extension is present (the format is in dxgiFormat) +extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DX10 = + { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('D','X','1','0'), 0, 0, 0, 0, 0 }; + +#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_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT +#define DDS_WIDTH 0x00000004 // DDSD_WIDTH + +#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE +#define DDS_SURFACE_FLAGS_MIPMAP 0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX + +#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 + +#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME + +// Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION +typedef enum DDS_RESOURCE_DIMENSION +{ + DDS_DIMENSION_TEXTURE1D = 2, + DDS_DIMENSION_TEXTURE2D = 3, + DDS_DIMENSION_TEXTURE3D = 4, +} DDS_RESOURCE_DIMENSION; + +// Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG +typedef enum DDS_RESOURCE_MISC_FLAG +{ + DDS_RESOURCE_MISC_TEXTURECUBE = 0x4L, +} DDS_RESOURCE_MISC_FLAG; + +typedef struct +{ + uint32_t dwSize; + uint32_t dwFlags; + uint32_t dwHeight; + uint32_t dwWidth; + uint32_t dwPitchOrLinearSize; + uint32_t dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwFlags + uint32_t dwMipMapCount; + uint32_t dwReserved1[11]; + DDS_PIXELFORMAT ddspf; + uint32_t dwCaps; + uint32_t dwCaps2; + uint32_t dwCaps3; + uint32_t dwCaps4; + uint32_t dwReserved2; +} DDS_HEADER; + +typedef struct +{ + DXGI_FORMAT dxgiFormat; + uint32_t resourceDimension; + uint32_t miscFlag; // see DDS_RESOURCE_MISC_FLAG + uint32_t arraySize; + uint32_t reserved; +} DDS_HEADER_DXT10; + +#pragma pack(pop) + +}; // namespace diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h new file mode 100644 index 0000000..1886e50 --- /dev/null +++ b/DirectXTex/DirectXTex.h @@ -0,0 +1,458 @@ +//------------------------------------------------------------------------------------- +// DirectXTex.h +// +// DirectX Texture Library +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#if defined(_MSC_VER) && (_MSC_VER > 1000) +#pragma once +#endif + +#pragma warning(push) +#pragma warning(disable : 4005) +#include +#pragma warning(pop) + +#include + +#include +#include + +#define DIRECTX_TEX_VERSION 100 + +namespace DirectX +{ + //--------------------------------------------------------------------------------- + // DXGI Format Utilities + bool IsValid( _In_ DXGI_FORMAT fmt ); + bool IsCompressed( _In_ DXGI_FORMAT fmt ); + bool IsPacked( _In_ DXGI_FORMAT fmt ); + bool IsVideo( _In_ DXGI_FORMAT fmt ); + bool IsSRGB( _In_ DXGI_FORMAT fmt ); + bool IsTypeless( _In_ DXGI_FORMAT fmt ); + + size_t BitsPerPixel( _In_ DXGI_FORMAT fmt ); + + enum CP_FLAGS + { + CP_FLAGS_NONE = 0x0, // Normal operation + CP_FLAGS_LEGACY_DWORD = 0x1, // Assume pitch is DWORD aligned instead of BYTE aligned + CP_FLAGS_24BPP = 0x10000, // Override with a legacy 24 bits-per-pixel format size + CP_FLAGS_16BPP = 0x20000, // Override with a legacy 16 bits-per-pixel format size + CP_FLAGS_8BPP = 0x40000, // Override with a legacy 8 bits-per-pixel format size + }; + + void ComputePitch( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, + _Out_ size_t& rowPitch, _Out_ size_t& slicePitch, _In_ DWORD flags = CP_FLAGS_NONE ); + + size_t ComputeScanlines( _In_ DXGI_FORMAT fmt, _In_ size_t height ); + + DXGI_FORMAT MakeSRGB( _In_ DXGI_FORMAT fmt ); + + //--------------------------------------------------------------------------------- + // Texture metadata + enum TEX_DIMENSION + // Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION + { + TEX_DIMENSION_TEXTURE1D = 2, + TEX_DIMENSION_TEXTURE2D = 3, + TEX_DIMENSION_TEXTURE3D = 4, + }; + + enum TEX_MISC_FLAG + // Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG + { + TEX_MISC_TEXTURECUBE = 0x4L, + }; + + struct TexMetadata + { + size_t width; + size_t height; // Should be 1 for 1D textures + size_t depth; // Should be 1 for 1D or 2D textures + size_t arraySize; // For cubemap, this is a multiple of 6 + size_t mipLevels; + uint32_t miscFlags; + DXGI_FORMAT format; + TEX_DIMENSION dimension; + + size_t ComputeIndex( _In_ size_t mip, _In_ size_t item, _In_ size_t slice ) const; + // Returns size_t(-1) to indicate an out-of-range error + }; + + enum DDS_FLAGS + { + DDS_FLAGS_NONE = 0x0, + + DDS_FLAGS_LEGACY_DWORD = 0x1, + // Assume pitch is DWORD aligned instead of BYTE aligned (used by some legacy DDS files) + + DDS_FLAGS_NO_LEGACY_EXPANSION = 0x2, + // Do not implicitly convert legacy formats that result in larger pixel sizes (24 bpp, 3:3:2, A8L8, A4L4, P8, A8P8) + + DDS_FLAGS_NO_R10B10G10A2_FIXUP = 0x4, + // Do not use work-around for long-standing D3DX DDS file format issue which reversed the 10:10:10:2 color order masks + + DDS_FLAGS_FORCE_RGB = 0x8, + // Convert DXGI 1.1 BGR formats to DXGI_FORMAT_R8G8B8A8_UNORM to avoid use of optional WDDM 1.1 formats + + DDS_FLAGS_NO_16BPP = 0x10, + // Conversions avoid use of 565, 5551, and 4444 formats and instead expand to 8888 to avoid use of optional WDDM 1.2 formats + + DDS_FLAGS_FORCE_DX10_EXT = 0x10000, + // Always use the 'DX10' header extension for DDS writer (i.e. don't try to write DX9 compatible DDS files) + }; + + enum WIC_FLAGS + { + WIC_FLAGS_NONE = 0x0, + + WIC_FLAGS_FORCE_RGB = 0x1, + // Loads DXGI 1.1 BGR formats as DXGI_FORMAT_R8G8B8A8_UNORM to avoid use of optional WDDM 1.1 formats + + WIC_FLAGS_NO_X2_BIAS = 0x2, + // Loads DXGI 1.1 X2 10:10:10:2 format as DXGI_FORMAT_R10G10B10A2_UNORM + + WIC_FLAGS_NO_16BPP = 0x4, + // Loads 565, 5551, and 4444 formats as 8888 to avoid use of optional WDDM 1.2 formats + + WIC_FLAGS_ALLOW_MONO = 0x8, + // Loads 1-bit monochrome (black & white) as R1_UNORM rather than 8-bit greyscale + + WIC_FLAGS_ALL_FRAMES = 0x10, + // Loads all images in a multi-frame file, converting/resizing to match the first frame as needed, defaults to 0th frame otherwise + + WIC_FLAGS_DITHER = 0x10000, + // Use ordered 4x4 dithering for any required conversions + + WIC_FLAGS_DITHER_DIFFUSION = 0x20000, + // Use error-diffusion dithering for any required conversions + + WIC_FLAGS_FILTER_POINT = 0x100000, + WIC_FLAGS_FILTER_LINEAR = 0x200000, + WIC_FLAGS_FILTER_CUBIC = 0x300000, + WIC_FLAGS_FILTER_FANT = 0x400000, // Combination of Linear and Box filter + // Filtering mode to use for any required image resizing (only needed when loading arrays of differently sized images; defaults to Fant) + }; + + HRESULT GetMetadataFromDDSMemory( _In_bytecount_(size) LPCVOID pSource, _In_ size_t size, _In_ DWORD flags, + _Out_ TexMetadata& metadata ); + HRESULT GetMetadataFromDDSFile( _In_z_ LPCWSTR szFile, DWORD flags, + _Out_ TexMetadata& metadata ); + + HRESULT GetMetadataFromTGAMemory( _In_bytecount_(size) LPCVOID pSource, _In_ size_t size, + _Out_ TexMetadata& metadata ); + HRESULT GetMetadataFromTGAFile( _In_z_ LPCWSTR szFile, + _Out_ TexMetadata& metadata ); + + HRESULT GetMetadataFromWICMemory( _In_bytecount_(size) LPCVOID pSource, _In_ size_t size, _In_ DWORD flags, + _Out_ TexMetadata& metadata ); + HRESULT GetMetadataFromWICFile( _In_z_ LPCWSTR szFile, _In_ DWORD flags, + _Out_ TexMetadata& metadata ); + + //--------------------------------------------------------------------------------- + // Bitmap image container + struct Image + { + size_t width; + size_t height; + DXGI_FORMAT format; + size_t rowPitch; + size_t slicePitch; + uint8_t* pixels; + }; + + class ScratchImage + { + public: + ScratchImage() : _nimages(0), _size(0), _image(0), _memory(0) {} + ~ScratchImage() { Release(); } + + HRESULT Initialize( _In_ const TexMetadata& mdata ); + + HRESULT Initialize1D( _In_ DXGI_FORMAT fmt, _In_ size_t length, _In_ size_t arraySize, _In_ size_t mipLevels ); + HRESULT Initialize2D( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t arraySize, _In_ size_t mipLevels ); + HRESULT Initialize3D( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t depth, _In_ size_t mipLevels ); + HRESULT InitializeCube( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t nCubes, _In_ size_t mipLevels ); + + HRESULT InitializeFromImage( _In_ const Image& srcImage, _In_ bool allow1D = false ); + HRESULT InitializeArrayFromImages( _In_count_(nImages) const Image* images, _In_ size_t nImages, _In_ bool allow1D = false ); + HRESULT InitializeCubeFromImages( _In_count_(nImages) const Image* images, _In_ size_t nImages ); + HRESULT Initialize3DFromImages( _In_count_(depth) const Image* images, _In_ size_t depth ); + + void Release(); + + const TexMetadata& GetMetadata() const { return _metadata; } + const Image* GetImage(_In_ size_t mip, _In_ size_t item, _In_ size_t slice) const; + + const Image* GetImages() const { return _image; } + size_t GetImageCount() const { return _nimages; } + + uint8_t* GetPixels() const { return _memory; } + size_t GetPixelsSize() const { return _size; } + + private: + size_t _nimages; + size_t _size; + TexMetadata _metadata; + Image* _image; + uint8_t* _memory; + + // Hide copy constructor and assignment operator + ScratchImage( const ScratchImage& ); + ScratchImage& operator=( const ScratchImage& ); + }; + + //--------------------------------------------------------------------------------- + // Memory blob (allocated buffer pointer is always 16-byte aligned) + class Blob + { + public: + Blob() : _buffer(0), _size(0) {} + ~Blob() { Release(); } + + HRESULT Initialize( _In_ size_t size ); + + void Release(); + + void *GetBufferPointer() const { return _buffer; } + size_t GetBufferSize() const { return _size; } + + private: + void* _buffer; + size_t _size; + + // Hide copy constructor and assignment operator + Blob( const Blob& ); + Blob& operator=( const Blob& ); + }; + + //--------------------------------------------------------------------------------- + // Image I/O + + // DDS operations + HRESULT LoadFromDDSMemory( _In_bytecount_(size) LPCVOID pSource, _In_ size_t size, _In_ DWORD flags, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image ); + HRESULT LoadFromDDSFile( _In_z_ LPCWSTR szFile, _In_ DWORD flags, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image ); + + HRESULT SaveToDDSMemory( _In_ const Image& image, _In_ DWORD flags, + _Out_ Blob& blob ); + HRESULT SaveToDDSMemory( _In_count_(nimages) const Image* images, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_ DWORD flags, + _Out_ Blob& blob ); + + HRESULT SaveToDDSFile( _In_ const Image& image, _In_ DWORD flags, _In_z_ LPCWSTR szFile ); + HRESULT SaveToDDSFile( _In_count_(nimages) const Image* images, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_ DWORD flags, _In_z_ LPCWSTR szFile ); + + // TGA operations + HRESULT LoadFromTGAMemory( _In_bytecount_(size) LPCVOID pSource, _In_ size_t size, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image ); + HRESULT LoadFromTGAFile( _In_z_ LPCWSTR szFile, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image ); + + HRESULT SaveToTGAMemory( _In_ const Image& image, _Out_ Blob& blob ); + HRESULT SaveToTGAFile( _In_ const Image& image, _In_z_ LPCWSTR szFile ); + + // WIC operations + HRESULT LoadFromWICMemory( _In_bytecount_(size) LPCVOID pSource, _In_ size_t size, _In_ DWORD flags, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image ); + HRESULT LoadFromWICFile( _In_z_ LPCWSTR szFile, _In_ DWORD flags, + _Out_opt_ TexMetadata* metadata, _Out_ ScratchImage& image ); + + HRESULT SaveToWICMemory( _In_ const Image& image, _In_ DWORD flags, _In_ REFGUID guidContainerFormat, + _Out_ Blob& blob ); + HRESULT SaveToWICMemory( _In_count_(nimages) const Image* images, _In_ size_t nimages, _In_ DWORD flags, _In_ REFGUID guidContainerFormat, _Out_ Blob& blob ); + + HRESULT SaveToWICFile( _In_ const Image& image, _In_ DWORD flags, _In_ REFGUID guidContainerFormat, _In_z_ LPCWSTR szFile ); + HRESULT SaveToWICFile( _In_count_(nimages) const Image* images, _In_ size_t nimages, _In_ DWORD flags, _In_ REFGUID guidContainerFormat, _In_z_ LPCWSTR szFile ); + + enum WICCodecs + { + WIC_CODEC_BMP =1, // Windows Bitmap (.bmp) + WIC_CODEC_JPEG, // Joint Photographic Experts Group (.jpg, .jpeg) + WIC_CODEC_PNG, // Portable Network Graphics (.png) + WIC_CODEC_TIFF, // Tagged Image File Format (.tif, .tiff) + WIC_CODEC_GIF, // Graphics Interchange Format (.gif) + WIC_CODEC_WMP, // Windows Media Photo / HD Photo / JPEG XR (.hdp, .jxr, .wdp) + WIC_CODEC_ICO, // Windows Icon (.ico) + }; + + REFGUID GetWICCodec( _In_ WICCodecs codec ); + + //--------------------------------------------------------------------------------- + // Texture conversion, resizing, mipmap generation, and block compression + + enum TEX_FR_FLAGS + { + TEX_FR_ROTATE0 = 0x0, + TEX_FR_ROTATE90 = 0x1, + TEX_FR_ROTATE180 = 0x2, + TEX_FR_ROTATE270 = 0x3, + TEX_FR_FLIP_HORIZONTAL = 0x08, + TEX_FR_FLIP_VERTICAL = 0x10, + }; + + HRESULT FlipRotate( _In_ const Image& srcImage, _In_ DWORD flags, _Out_ ScratchImage& image ); + HRESULT FlipRotate( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DWORD flags, _Out_ ScratchImage& result ); + // Flip and/or rotate image + + enum TEX_FILTER_FLAGS + { + TEX_FILTER_DEFAULT = 0, + + // Clamp filtering only + + TEX_FILTER_SEPARATE_ALPHA = 0x100, + // Resize color and alpha channel independently + + TEX_FILTER_DITHER = 0x10000, + // Use ordered 4x4 dithering for any required conversions + TEX_FILTER_DITHER_DIFFUSION = 0x20000, + // Use error-diffusion dithering for any required conversions + + TEX_FILTER_POINT = 0x100000, + TEX_FILTER_LINEAR = 0x200000, + TEX_FILTER_CUBIC = 0x300000, + TEX_FILTER_FANT = 0x400000, // Equiv to Box filtering for mipmap generation + // Filtering mode to use for any required image resizing + + TEX_FILTER_SRGB_IN = 0x1000000, + TEX_FILTER_SRGB_OUT = 0x2000000, + TEX_FILTER_SRGB = 0x3000000, + // sRGB <-> RGB for use in conversion operations + // if the input format type is IsSRGB(), then SRGB_IN is on by default + // if the output format type is IsSRGB(), then SRGB_OUT is on by default + }; + + HRESULT Resize( _In_ const Image& srcImage, _In_ size_t width, _In_ size_t height, _In_ DWORD filter, + _Out_ ScratchImage& image ); + HRESULT Resize( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ size_t width, _In_ size_t height, _In_ DWORD filter, _Out_ ScratchImage& result ); + // Resize the image to width x height. Defaults to Fant filtering. + // Note for a complex resize, the result will always have mipLevels == 1 + + HRESULT Convert( _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ DWORD filter, _In_ float threshold, + _Out_ ScratchImage& image ); + HRESULT Convert( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DXGI_FORMAT format, _In_ DWORD filter, _In_ float threshold, _Out_ ScratchImage& result ); + // Convert the image to a new format + + HRESULT GenerateMipMaps( _In_ const Image& baseImage, _In_ DWORD filter, _In_ size_t levels, + _Out_ ScratchImage& mipChain, bool allow1D = false ); + HRESULT GenerateMipMaps( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DWORD filter, _In_ size_t levels, _Out_ ScratchImage& mipChain ); + // levels of '0' indicates a full mipchain, otherwise is generates that number of total levels (including the source base image) + // Defaults to Fant filtering which is equivalent to a box filter + + HRESULT GenerateMipMaps3D( _In_count_(depth) const Image* baseImages, _In_ size_t depth, _In_ DWORD filter, _In_ size_t levels, + _Out_ ScratchImage& mipChain ); + HRESULT GenerateMipMaps3D( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DWORD filter, _In_ size_t levels, _Out_ ScratchImage& mipChain ); + // levels of '0' indicates a full mipchain, otherwise is generates that number of total levels (including the source base image) + // Defaults to Fant filtering which is equivalent to a box filter + + enum TEX_COMPRESS_FLAGS + { + TEX_COMPRESS_DEFAULT = 0, + + TEX_COMPRESS_RGB_DITHER = 0x10000, + // Enables dithering RGB colors for BC1-3 compression + + TEX_COMPRESS_A_DITHER = 0x20000, + // Enables dithering alpha for BC1-3 compression + + TEX_COMPRESS_DITHER = 0x30000, + // Enables both RGB and alpha dithering for BC1-3 compression + + TEX_COMPRESS_UNIFORM = 0x40000, + // Uniform color weighting for BC1-3 compression; by default uses perceptual weighting + + TEX_COMPRESS_PARALLEL = 0x10000000, + // Compress is free to use multithreading to improve performance (by default it does not use multithreading) + }; + + HRESULT Compress( _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ DWORD compress, _In_ float alphaRef, + _Out_ ScratchImage& cImage ); + HRESULT Compress( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DXGI_FORMAT format, _In_ DWORD compress, _In_ float alphaRef, _Out_ ScratchImage& cImages ); + // Note that alphaRef is only used by BC1. 0.5f is a typical value to use + + HRESULT Decompress( _In_ const Image& cImage, _In_ DXGI_FORMAT format, _Out_ ScratchImage& image ); + HRESULT Decompress( _In_count_(nimages) const Image* cImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DXGI_FORMAT format, _Out_ ScratchImage& images ); + + //--------------------------------------------------------------------------------- + // Normal map operations + + enum CNMAP_FLAGS + { + CNMAP_DEFAULT = 0, + + CNMAP_CHANNEL_RED = 0x1, + CNMAP_CHANNEL_GREEN = 0x2, + CNMAP_CHANNEL_BLUE = 0x3, + CNMAP_CHANNEL_ALPHA = 0x4, + CNMAP_CHANNEL_LUMINANCE = 0x5, + // Channel selection when evaluting color value for height + // Luminance is a combination of red, green, and blue + + CNMAP_MIRROR_U = 0x1000, + CNMAP_MIRROR_V = 0x2000, + CNMAP_MIRROR = 0x3000, + // Use mirror semantics for scanline references (defaults to wrap) + + CNMAP_INVERT_SIGN = 0x4000, + // Inverts normal sign + + CNMAP_COMPUTE_OCCLUSION = 0x8000, + // Computes a crude occlusion term stored in the alpha channel + }; + + HRESULT ComputeNormalMap( _In_ const Image& srcImage, _In_ DWORD flags, _In_ float amplitude, + _In_ DXGI_FORMAT format, _Out_ ScratchImage& normalMap ); + HRESULT ComputeNormalMap( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DWORD flags, _In_ float amplitude, _In_ DXGI_FORMAT format, _Out_ ScratchImage& normalMaps ); + + //--------------------------------------------------------------------------------- + // Misc image operations + struct Rect + { + size_t x; + size_t y; + size_t w; + size_t h; + + Rect() {} + Rect( size_t _x, size_t _y, size_t _w, size_t _h ) : x(_x), y(_y), w(_w), h(_h) {} + }; + + HRESULT CopyRectangle( _In_ const Image& srcImage, _In_ const Rect& srcRect, _In_ const Image& dstImage, + _In_ DWORD filter, _In_ size_t xOffset, _In_ size_t yOffset ); + + HRESULT ComputeMSE( _In_ const Image& image1, _In_ const Image& image2, _Out_ float& mse, _Out_opt_cap_c_(4) float* mseV ); + + //--------------------------------------------------------------------------------- + // Direct3D 11 functions + bool IsSupportedTexture( _In_ ID3D11Device* pDevice, _In_ const TexMetadata& metadata ); + + HRESULT CreateTexture( _In_ ID3D11Device* pDevice, _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _Deref_out_ ID3D11Resource** ppResource ); + + HRESULT CreateShaderResourceView( _In_ ID3D11Device* pDevice, _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _Deref_out_ ID3D11ShaderResourceView** ppSRV ); + + HRESULT CaptureTexture( _In_ ID3D11Device* pDevice, _In_ ID3D11DeviceContext* pContext, _In_ ID3D11Resource* pSource, _Out_ ScratchImage& result ); + +#include "DirectXTex.inl" + +}; // namespace diff --git a/DirectXTex/DirectXTex.inl b/DirectXTex/DirectXTex.inl new file mode 100644 index 0000000..781f90d --- /dev/null +++ b/DirectXTex/DirectXTex.inl @@ -0,0 +1,223 @@ +//------------------------------------------------------------------------------------- +// DirectXTex.inl +// +// DirectX Texture Library +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#if defined(_MSC_VER) && (_MSC_VER > 1000) +#pragma once +#endif + +//===================================================================================== +// DXGI Format Utilities +//===================================================================================== + +inline bool IsValid( DXGI_FORMAT fmt ) +{ +#ifdef DXGI_1_2_FORMATS + return ( static_cast(fmt) >= 1 && static_cast(fmt) <= 115 ); +#else + return ( static_cast(fmt) >= 1 && static_cast(fmt) <= 99 ); +#endif +} + +inline bool IsCompressed( DXGI_FORMAT fmt ) +{ + switch ( fmt ) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return true; + + default: + return false; + } +} + +inline bool IsPacked( DXGI_FORMAT fmt ) +{ + return ( (fmt == DXGI_FORMAT_R8G8_B8G8_UNORM) || (fmt == DXGI_FORMAT_G8R8_G8B8_UNORM) ); +} + +inline bool IsVideo( DXGI_FORMAT fmt ) +{ +#ifdef DXGI_1_2_FORMATS + switch ( fmt ) + { + case DXGI_FORMAT_AYUV: + case DXGI_FORMAT_Y410: + case DXGI_FORMAT_Y416: + case DXGI_FORMAT_NV12: + case DXGI_FORMAT_P010: + case DXGI_FORMAT_P016: + case DXGI_FORMAT_YUY2: + case DXGI_FORMAT_Y210: + case DXGI_FORMAT_Y216: + case DXGI_FORMAT_NV11: + // These video formats can be used with the 3D pipeline through special view mappings + return true; + + case DXGI_FORMAT_420_OPAQUE: + case DXGI_FORMAT_AI44: + case DXGI_FORMAT_IA44: + case DXGI_FORMAT_P8: + case DXGI_FORMAT_A8P8: + // These are limited use video formats not usable in any way by the 3D pipeline + return true; + + default: + return false; + } +#else // !DXGI_1_2_FORMATS + UNREFERENCED_PARAMETER(fmt); + return false; +#endif +} + +inline bool IsSRGB( DXGI_FORMAT fmt ) +{ + switch( fmt ) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return true; + + default: + return false; + } +} + +inline bool IsTypeless( DXGI_FORMAT fmt ) +{ + switch( fmt ) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC7_TYPELESS: + return true; + + default: + return false; + } +} + +inline size_t ComputeScanlines( _In_ DXGI_FORMAT fmt, _In_ size_t height ) +{ + switch ( fmt ) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return std::max( 1, (height + 3) / 4 ); + + default: + return height; + } +} + +//===================================================================================== +// Image I/O +//===================================================================================== +inline HRESULT SaveToDDSMemory( const Image& image, DWORD flags, Blob& blob ) +{ + TexMetadata mdata; + memset( &mdata, 0, sizeof(mdata) ); + mdata.width = image.width; + mdata.height = image.height; + mdata.depth = 1; + mdata.arraySize = 1; + mdata.mipLevels = 1; + mdata.format = image.format; + mdata.dimension = TEX_DIMENSION_TEXTURE2D; + + return SaveToDDSMemory( &image, 1, mdata, flags, blob ); +} + +inline HRESULT SaveToDDSFile( const Image& image, DWORD flags, LPCWSTR szFile ) +{ + TexMetadata mdata; + memset( &mdata, 0, sizeof(mdata) ); + mdata.width = image.width; + mdata.height = image.height; + mdata.depth = 1; + mdata.arraySize = 1; + mdata.mipLevels = 1; + mdata.format = image.format; + mdata.dimension = TEX_DIMENSION_TEXTURE2D; + + return SaveToDDSFile( &image, 1, mdata, flags, szFile ); +} diff --git a/DirectXTex/DirectXTexCompress.cpp b/DirectXTex/DirectXTexCompress.cpp new file mode 100644 index 0000000..573d3ad --- /dev/null +++ b/DirectXTex/DirectXTexCompress.cpp @@ -0,0 +1,697 @@ +//------------------------------------------------------------------------------------- +// DirectXTexCompress.cpp +// +// DirectX Texture Library - Texture compression +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +#ifdef _OPENMP +#include +#pragma warning(disable : 4616 6001 6993) +#endif + +#include "bc.h" + +namespace DirectX +{ + +inline static DWORD _GetBCFlags( _In_ DWORD compress ) +{ + static_assert( TEX_COMPRESS_RGB_DITHER == BC_FLAGS_DITHER_RGB, "TEX_COMPRESS_* flags should match BC_FLAGS_*" ); + static_assert( TEX_COMPRESS_A_DITHER == BC_FLAGS_DITHER_A, "TEX_COMPRESS_* flags should match BC_FLAGS_*" ); + static_assert( TEX_COMPRESS_DITHER == (BC_FLAGS_DITHER_RGB | BC_FLAGS_DITHER_A), "TEX_COMPRESS_* flags should match BC_FLAGS_*" ); + static_assert( TEX_COMPRESS_UNIFORM == BC_FLAGS_UNIFORM, "TEX_COMPRESS_* flags should match BC_FLAGS_*" ); + return ( compress & (BC_FLAGS_DITHER_RGB|BC_FLAGS_DITHER_A|BC_FLAGS_UNIFORM) ); +} + + +//------------------------------------------------------------------------------------- +static HRESULT _CompressBC( _In_ const Image& image, _In_ const Image& result, _In_ DWORD bcflags, + _In_ float alphaRef, _In_ bool degenerate ) +{ + if ( !image.pixels || !result.pixels ) + return E_POINTER; + + assert( image.width == result.width ); + assert( image.height == result.height ); + + const DXGI_FORMAT format = image.format; + size_t sbpp = BitsPerPixel( format ); + if ( !sbpp ) + return E_FAIL; + + if ( sbpp < 8 ) + { + // We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + // Round to bytes + sbpp = ( sbpp + 7 ) / 8; + + uint8_t *pDest = result.pixels; + + // Determine BC format encoder + BC_ENCODE pfEncode; + size_t blocksize; + switch(result.format) + { + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: pfEncode = nullptr; blocksize = 8; break; + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: pfEncode = D3DXEncodeBC2; blocksize = 16; break; + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: pfEncode = D3DXEncodeBC3; blocksize = 16; break; + case DXGI_FORMAT_BC4_UNORM: pfEncode = D3DXEncodeBC4U; blocksize = 8; break; + case DXGI_FORMAT_BC4_SNORM: pfEncode = D3DXEncodeBC4S; blocksize = 8; break; + case DXGI_FORMAT_BC5_UNORM: pfEncode = D3DXEncodeBC5U; blocksize = 16; break; + case DXGI_FORMAT_BC5_SNORM: pfEncode = D3DXEncodeBC5S; blocksize = 16; break; + case DXGI_FORMAT_BC6H_UF16: pfEncode = D3DXEncodeBC6HU; blocksize = 16; break; + case DXGI_FORMAT_BC6H_SF16: pfEncode = D3DXEncodeBC6HS; blocksize = 16; break; + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: pfEncode = D3DXEncodeBC7; blocksize = 16; break; + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + XMVECTOR temp[16]; + const uint8_t *pSrc = image.pixels; + const size_t rowPitch = image.rowPitch; + for( size_t h=0; h < image.height; h += 4 ) + { + const uint8_t *sptr = pSrc; + uint8_t* dptr = pDest; + for( size_t count = 0; count < rowPitch; count += sbpp*4 ) + { + if ( !_LoadScanline( &temp[0], 4, sptr, rowPitch, format ) ) + return E_FAIL; + + if ( image.height > 1 ) + { + if ( !_LoadScanline( &temp[4], 4, sptr + rowPitch, rowPitch, format ) ) + return E_FAIL; + + if ( image.height > 2 ) + { + if ( !_LoadScanline( &temp[8], 4, sptr + rowPitch*2, rowPitch, format ) ) + return E_FAIL; + + if ( !_LoadScanline( &temp[12], 4, sptr + rowPitch*3, rowPitch, format ) ) + return E_FAIL; + } + } + + if ( degenerate ) + { + assert( image.width < 4 || image.height < 4 ); + const size_t uSrc[] = { 0, 0, 0, 1 }; + + if ( image.width < 4 ) + { + for( size_t t=0; t < image.height && t < 4; ++t ) + { + for( size_t s = image.width; s < 4; ++s ) + { + temp[ t*4 + s ] = temp[ t*4 + uSrc[s] ]; + } + } + } + + if ( image.height < 4 ) + { + for( size_t t=image.height; t < 4; ++t ) + { + for( size_t s =0; s < 4; ++s ) + { + temp[ t*4 + s ] = temp[ uSrc[t]*4 + s ]; + } + } + } + } + + _ConvertScanline( temp, 16, result.format, format, 0 ); + + if ( pfEncode ) + pfEncode( dptr, temp, bcflags ); + else + D3DXEncodeBC1( dptr, temp, alphaRef, bcflags ); + + sptr += sbpp*4; + dptr += blocksize; + } + + pSrc += rowPitch*4; + pDest += result.rowPitch; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +#ifdef _OPENMP +static HRESULT _CompressBC_Parallel( _In_ const Image& image, _In_ const Image& result, _In_ DWORD bcflags, + _In_ float alphaRef ) +{ + if ( !image.pixels || !result.pixels ) + return E_POINTER; + + // Parallel version doesn't support degenerate case + assert( ((image.width % 4) == 0) && ((image.height % 4) == 0 ) ); + + assert( image.width == result.width ); + assert( image.height == result.height ); + + const DXGI_FORMAT format = image.format; + size_t sbpp = BitsPerPixel( format ); + if ( !sbpp ) + return E_FAIL; + + if ( sbpp < 8 ) + { + // We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + // Round to bytes + sbpp = ( sbpp + 7 ) / 8; + + // Determine BC format encoder + BC_ENCODE pfEncode; + size_t blocksize; + switch(result.format) + { + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: pfEncode = nullptr; blocksize = 8; break; + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: pfEncode = D3DXEncodeBC2; blocksize = 16; break; + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: pfEncode = D3DXEncodeBC3; blocksize = 16; break; + case DXGI_FORMAT_BC4_UNORM: pfEncode = D3DXEncodeBC4U; blocksize = 8; break; + case DXGI_FORMAT_BC4_SNORM: pfEncode = D3DXEncodeBC4S; blocksize = 8; break; + case DXGI_FORMAT_BC5_UNORM: pfEncode = D3DXEncodeBC5U; blocksize = 16; break; + case DXGI_FORMAT_BC5_SNORM: pfEncode = D3DXEncodeBC5S; blocksize = 16; break; + case DXGI_FORMAT_BC6H_UF16: pfEncode = D3DXEncodeBC6HU; blocksize = 16; break; + case DXGI_FORMAT_BC6H_SF16: pfEncode = D3DXEncodeBC6HS; blocksize = 16; break; + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: pfEncode = D3DXEncodeBC7; blocksize = 16; break; + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + // Refactored version of loop to support parallel independance + const size_t nBlocks = std::max(1, image.width / 4) * std::max(1, image.height / 4); + + bool fail = false; + +#pragma omp parallel for + for( int nb=0; nb < static_cast( nBlocks ); ++nb ) + { + const size_t nbWidth = std::max(1, image.width / 4); + + const size_t y = nb / nbWidth; + const size_t x = nb - (y*nbWidth); + + assert( x < image.width && y < image.height ); + + size_t rowPitch = image.rowPitch; + const uint8_t *pSrc = image.pixels + (y*4*rowPitch) + (x*4*sbpp); + + uint8_t *pDest = result.pixels + (nb*blocksize); + + XMVECTOR temp[16]; + if ( !_LoadScanline( &temp[0], 4, pSrc, rowPitch, format ) ) + fail = true; + + if ( !_LoadScanline( &temp[4], 4, pSrc + rowPitch, rowPitch, format ) ) + fail = true; + + if ( !_LoadScanline( &temp[8], 4, pSrc + rowPitch*2, rowPitch, format ) ) + fail = true; + + if ( !_LoadScanline( &temp[12], 4, pSrc + rowPitch*3, rowPitch, format ) ) + fail = true; + + _ConvertScanline( temp, 16, result.format, format, 0 ); + + if ( pfEncode ) + pfEncode( pDest, temp, bcflags ); + else + D3DXEncodeBC1( pDest, temp, alphaRef, bcflags ); + } + + return (fail) ? E_FAIL : S_OK; +} + +#endif // _OPENMP + + +//------------------------------------------------------------------------------------- +static DXGI_FORMAT _DefaultDecompress( _In_ DXGI_FORMAT format ) +{ + switch( format ) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM; + + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + return DXGI_FORMAT_R8_UNORM; + + case DXGI_FORMAT_BC4_SNORM: + return DXGI_FORMAT_R8_SNORM; + + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + return DXGI_FORMAT_R8G8_UNORM; + + case DXGI_FORMAT_BC5_SNORM: + return DXGI_FORMAT_R8G8_SNORM; + + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + // We could use DXGI_FORMAT_R32G32B32_FLOAT here since BC6H is always Alpha 1.0, + // but this format is more supported by viewers + return DXGI_FORMAT_R32G32B32A32_FLOAT; + + default: + return DXGI_FORMAT_UNKNOWN; + } +} + + +//------------------------------------------------------------------------------------- +static HRESULT _DecompressBC( _In_ const Image& cImage, _In_ const Image& result ) +{ + if ( !cImage.pixels || !result.pixels ) + return E_POINTER; + + assert( cImage.width == result.width ); + assert( cImage.height == result.height ); + + // Image must be a multiple of 4 (degenerate cases of 1x1, 1x2, 2x1, and 2x2 are allowed) + size_t width = cImage.width; + if ( (width % 4) != 0 ) + { + if ( width != 1 && width != 2 ) + return E_INVALIDARG; + } + + size_t height = cImage.height; + if ( (height % 4) != 0 ) + { + if ( height != 1 && height != 2 ) + return E_INVALIDARG; + } + + const DXGI_FORMAT format = result.format; + size_t dbpp = BitsPerPixel( format ); + if ( !dbpp ) + return E_FAIL; + + if ( dbpp < 8 ) + { + // We don't support decompressing to monochrome (DXGI_FORMAT_R1_UNORM) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + // Round to bytes + dbpp = ( dbpp + 7 ) / 8; + + uint8_t *pDest = result.pixels; + if ( !pDest ) + return E_POINTER; + + // Promote "typeless" BC formats + DXGI_FORMAT cformat; + switch( cImage.format ) + { + case DXGI_FORMAT_BC1_TYPELESS: cformat = DXGI_FORMAT_BC1_UNORM; break; + case DXGI_FORMAT_BC2_TYPELESS: cformat = DXGI_FORMAT_BC2_UNORM; break; + case DXGI_FORMAT_BC3_TYPELESS: cformat = DXGI_FORMAT_BC3_UNORM; break; + case DXGI_FORMAT_BC4_TYPELESS: cformat = DXGI_FORMAT_BC4_UNORM; break; + case DXGI_FORMAT_BC5_TYPELESS: cformat = DXGI_FORMAT_BC5_UNORM; break; + case DXGI_FORMAT_BC6H_TYPELESS: cformat = DXGI_FORMAT_BC6H_UF16; break; + case DXGI_FORMAT_BC7_TYPELESS: cformat = DXGI_FORMAT_BC7_UNORM; break; + default: cformat = cImage.format; break; + } + + // Determine BC format decoder + BC_DECODE pfDecode; + size_t sbpp; + switch(cformat) + { + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: pfDecode = D3DXDecodeBC1; sbpp = 8; break; + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: pfDecode = D3DXDecodeBC2; sbpp = 16; break; + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: pfDecode = D3DXDecodeBC3; sbpp = 16; break; + case DXGI_FORMAT_BC4_UNORM: pfDecode = D3DXDecodeBC4U; sbpp = 8; break; + case DXGI_FORMAT_BC4_SNORM: pfDecode = D3DXDecodeBC4S; sbpp = 8; break; + case DXGI_FORMAT_BC5_UNORM: pfDecode = D3DXDecodeBC5U; sbpp = 16; break; + case DXGI_FORMAT_BC5_SNORM: pfDecode = D3DXDecodeBC5S; sbpp = 16; break; + case DXGI_FORMAT_BC6H_UF16: pfDecode = D3DXDecodeBC6HU; sbpp = 16; break; + case DXGI_FORMAT_BC6H_SF16: pfDecode = D3DXDecodeBC6HS; sbpp = 16; break; + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: pfDecode = D3DXDecodeBC7; sbpp = 16; break; + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + XMVECTOR temp[16]; + const uint8_t *pSrc = cImage.pixels; + const size_t rowPitch = result.rowPitch; + for( size_t h=0; h < cImage.height; h += 4 ) + { + const uint8_t *sptr = pSrc; + uint8_t* dptr = pDest; + for( size_t count = 0; count < cImage.rowPitch; count += sbpp ) + { + pfDecode( temp, sptr ); + _ConvertScanline( temp, 16, format, cformat, 0 ); + + if ( !_StoreScanline( dptr, rowPitch, format, &temp[0], 4 ) ) + return E_FAIL; + + if ( result.height > 1 ) + { + if ( !_StoreScanline( dptr + rowPitch, rowPitch, format, &temp[4], 4 ) ) + return E_FAIL; + + if ( result.height > 2 ) + { + if ( !_StoreScanline( dptr + rowPitch*2, rowPitch, format, &temp[8], 4 ) ) + return E_FAIL; + + if ( !_StoreScanline( dptr + rowPitch*3, rowPitch, format, &temp[12], 4 ) ) + return E_FAIL; + } + } + + sptr += sbpp; + dptr += dbpp*4; + } + + pSrc += cImage.rowPitch; + pDest += rowPitch*4; + } + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Compression +//------------------------------------------------------------------------------------- +HRESULT Compress( const Image& srcImage, DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& image ) +{ + if ( IsCompressed(srcImage.format) || !IsCompressed(format) || IsTypeless(format) ) + return E_INVALIDARG; + + // Image size must be a multiple of 4 (degenerate cases for mipmaps are allowed) + bool degenerate = false; + + size_t width = srcImage.width; + if ( (width % 4) != 0 ) + { + if ( width != 1 && width != 2 ) + return E_INVALIDARG; + + degenerate = true; + } + + size_t height = srcImage.height; + if ( (height % 4) != 0 ) + { + if ( height != 1 && height != 2 ) + return E_INVALIDARG; + + degenerate = true; + } + + // Create compressed image + HRESULT hr = image.Initialize2D( format, width, height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *img = image.GetImage( 0, 0, 0 ); + if ( !img ) + { + image.Release(); + return E_POINTER; + } + + // Compress single image + if ( (compress & TEX_COMPRESS_PARALLEL) && !degenerate ) + { +#ifndef _OPENMP + return E_NOTIMPL; +#else + hr = _CompressBC_Parallel( srcImage, *img, _GetBCFlags( compress ), alphaRef ); +#endif // _OPENMP + } + else + { + hr = _CompressBC( srcImage, *img, _GetBCFlags( compress ), alphaRef, degenerate ); + } + + if ( FAILED(hr) ) + image.Release(); + + return hr; +} + +HRESULT Compress( const Image* srcImages, size_t nimages, const TexMetadata& metadata, + DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& cImages ) +{ + if ( !srcImages || !nimages ) + return E_INVALIDARG; + + if ( !IsCompressed(format) || IsTypeless(format) ) + return E_INVALIDARG; + + // Image size must be a multiple of 4 (degenerate cases for mipmaps are allowed) + size_t width = srcImages[0].width; + if ( (width % 4) != 0 ) + { + if ( width != 1 && width != 2 ) + return E_INVALIDARG; + } + + size_t height = srcImages[0].height; + if ( (height % 4) != 0 ) + { + if ( height != 1 && height != 2 ) + return E_INVALIDARG; + } + + cImages.Release(); + + TexMetadata mdata2 = metadata; + mdata2.format = format; + HRESULT hr = cImages.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + if ( nimages != cImages.GetImageCount() ) + { + cImages.Release(); + return E_FAIL; + } + + const Image* dest = cImages.GetImages(); + if ( !dest ) + { + cImages.Release(); + return E_POINTER; + } + + for( size_t index=0; index < nimages; ++index ) + { + assert( dest[ index ].format == format ); + + const Image& src = srcImages[ index ]; + + height = src.height; + width = src.width; + if ( width != dest[ index ].width || height != dest[ index ].height ) + { + cImages.Release(); + return E_FAIL; + } + + bool degenerate = ((height < 4) || (width < 4)) != 0; + + if ( (compress & TEX_COMPRESS_PARALLEL) && !degenerate) + { +#ifndef _OPENMP + return E_NOTIMPL; +#else + if ( compress & TEX_COMPRESS_PARALLEL ) + { + hr = _CompressBC_Parallel( src, dest[ index ], _GetBCFlags( compress ), alphaRef ); + if ( FAILED(hr) ) + { + cImages.Release(); + return hr; + } + } +#endif // _OPENMP + } + else + { + hr = _CompressBC( src, dest[ index ], _GetBCFlags( compress ), alphaRef, degenerate ); + if ( FAILED(hr) ) + { + cImages.Release(); + return hr; + } + } + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Decompression +//------------------------------------------------------------------------------------- +HRESULT Decompress( const Image& cImage, DXGI_FORMAT format, ScratchImage& image ) +{ + if ( IsCompressed(format) || IsTypeless(format) ) + return E_INVALIDARG; + + if ( format == DXGI_FORMAT_UNKNOWN ) + { + // Pick a default decompressed format based on BC input format + format = _DefaultDecompress( cImage.format ); + if ( format == DXGI_FORMAT_UNKNOWN ) + { + // Input is not a compressed format + return E_INVALIDARG; + } + } + else if ( !IsCompressed(cImage.format) || !IsValid(format) ) + return E_INVALIDARG; + + // Create decompressed image + HRESULT hr = image.Initialize2D( format, cImage.width, cImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *img = image.GetImage( 0, 0, 0 ); + if ( !img ) + { + image.Release(); + return E_POINTER; + } + + // Decompress single image + hr = _DecompressBC( cImage, *img ); + if ( FAILED(hr) ) + image.Release(); + + return hr; +} + +HRESULT Decompress( const Image* cImages, size_t nimages, const TexMetadata& metadata, + DXGI_FORMAT format, ScratchImage& images ) +{ + if ( !cImages || !nimages ) + return E_INVALIDARG; + + if ( IsCompressed(format) || IsTypeless(format) ) + return E_INVALIDARG; + + if ( format == DXGI_FORMAT_UNKNOWN ) + { + // Pick a default decompressed format based on BC input format + format = _DefaultDecompress( cImages[0].format ); + if ( format == DXGI_FORMAT_UNKNOWN ) + { + // Input is not a compressed format + return E_FAIL; + } + } + else if ( !IsValid(format) ) + return E_INVALIDARG; + + images.Release(); + + TexMetadata mdata2 = metadata; + mdata2.format = format; + HRESULT hr = images.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + if ( nimages != images.GetImageCount() ) + { + images.Release(); + return E_FAIL; + } + + const Image* dest = images.GetImages(); + if ( !dest ) + { + images.Release(); + return E_POINTER; + } + + for( size_t index=0; index < nimages; ++index ) + { + assert( dest[ index ].format == format ); + + const Image& src = cImages[ index ]; + if ( !IsCompressed( src.format ) ) + { + images.Release(); + return E_FAIL; + } + + if ( src.width != dest[ index ].width || src.height != dest[ index ].height ) + { + images.Release(); + return E_FAIL; + } + + hr = _DecompressBC( src, dest[ index ] ); + if ( FAILED(hr) ) + { + images.Release(); + return hr; + } + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexConvert.cpp b/DirectXTex/DirectXTexConvert.cpp new file mode 100644 index 0000000..78383a9 --- /dev/null +++ b/DirectXTex/DirectXTexConvert.cpp @@ -0,0 +1,2393 @@ +//------------------------------------------------------------------------------------- +// DirectXTexConvert.cpp +// +// DirectX Texture Library - Image conversion +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +#ifdef USE_XNAMATH +#if XNAMATH_VERSION < 204 +#error This file requires XNAMATH v2.04 or later +#endif +#else +using namespace DirectX::PackedVector; +#endif + +namespace DirectX +{ + +//------------------------------------------------------------------------------------- +// Copies an image row with optional clearing of alpha value to 1.0 +// (can be used in place as well) otherwise copies the image row unmodified. +//------------------------------------------------------------------------------------- +void _CopyScanline( LPVOID pDestination, size_t outSize, LPCVOID pSource, size_t inSize, DXGI_FORMAT format, DWORD flags ) +{ + assert( pDestination && outSize > 0 ); + assert( pSource && inSize > 0 ); + assert( IsValid(format) && !IsVideo(format) ); + + if ( flags & TEXP_SCANLINE_SETALPHA ) + { + switch( format ) + { + //----------------------------------------------------------------------------- + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + { + uint32_t alpha; + if ( format == DXGI_FORMAT_R32G32B32A32_FLOAT ) + alpha = 0x3f800000; + else if ( format == DXGI_FORMAT_R32G32B32A32_SINT ) + alpha = 0x7fffffff; + else + alpha = 0xffffffff; + + if ( pDestination == pSource ) + { + uint32_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 16 ) + { + dPtr += 3; + *(dPtr++) = alpha; + } + } + else + { + const uint32_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 16 ) + { + *(dPtr++) = *(sPtr++); + *(dPtr++) = *(sPtr++); + *(dPtr++) = *(sPtr++); + *(dPtr++) = alpha; + sPtr++; + } + } + } + return; + + //----------------------------------------------------------------------------- + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + { + uint16_t alpha; + if ( format == DXGI_FORMAT_R16G16B16A16_FLOAT ) + alpha = 0x3c00; + else if ( format == DXGI_FORMAT_R16G16B16A16_SNORM || format == DXGI_FORMAT_R16G16B16A16_SINT ) + alpha = 0x7fff; + else + alpha = 0xffff; + + if ( pDestination == pSource ) + { + uint16_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 8 ) + { + dPtr += 3; + *(dPtr++) = alpha; + } + } + else + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + uint16_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 8 ) + { + *(dPtr++) = *(sPtr++); + *(dPtr++) = *(sPtr++); + *(dPtr++) = *(sPtr++); + *(dPtr++) = alpha; + sPtr++; + } + } + } + return; + + //----------------------------------------------------------------------------- + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + if ( pDestination == pSource ) + { + uint32_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 4 ) + { + *dPtr |= 0xC0000000; + ++dPtr; + } + } + else + { + const uint32_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 4 ) + { + *(dPtr++) = *(sPtr++) | 0xC0000000; + } + } + return; + + //----------------------------------------------------------------------------- + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + { + const uint32_t alpha = ( format == DXGI_FORMAT_R8G8B8A8_SNORM || format == DXGI_FORMAT_R8G8B8A8_SINT ) ? 0x7f000000 : 0xff000000; + + if ( pDestination == pSource ) + { + uint32_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 4 ) + { + uint32_t t = *dPtr & 0xFFFFFF; + t |= alpha; + *(dPtr++) = t; + } + } + else + { + const uint32_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 4 ) + { + uint32_t t = *(sPtr++) & 0xFFFFFF; + t |= alpha; + *(dPtr++) = t; + } + } + } + return; + + //----------------------------------------------------------------------------- + case DXGI_FORMAT_B5G5R5A1_UNORM: + if ( pDestination == pSource ) + { + uint16_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 2 ) + { + *(dPtr++) |= 0x8000; + } + } + else + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + uint16_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 2 ) + { + *(dPtr++) = *(sPtr++) | 0x8000; + } + } + return; + + //----------------------------------------------------------------------------- + case DXGI_FORMAT_A8_UNORM: + memset( pDestination, 0xff, outSize ); + return; + +#ifdef DXGI_1_2_FORMATS + //----------------------------------------------------------------------------- + case DXGI_FORMAT_B4G4R4A4_UNORM: + if ( pDestination == pSource ) + { + uint16_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 2 ) + { + *(dPtr++) |= 0xF000; + } + } + else + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + uint16_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 2 ) + { + *(dPtr++) = *(sPtr++) | 0xF000; + } + } + return; +#endif // DXGI_1_2_FORMATS + } + } + + // Fall-through case is to just use memcpy (assuming this is not an in-place operation) + if ( pDestination == pSource ) + return; + + size_t size = std::min( outSize, inSize ); + memcpy_s( pDestination, outSize, pSource, size ); +} + + +//------------------------------------------------------------------------------------- +// Swizzles (RGB <-> BGR) an image row with optional clearing of alpha value to 1.0 +// (can be used in place as well) otherwise copies the image row unmodified. +//------------------------------------------------------------------------------------- +void _SwizzleScanline( LPVOID pDestination, size_t outSize, LPCVOID pSource, size_t inSize, DXGI_FORMAT format, DWORD flags ) +{ + assert( pDestination && outSize > 0 ); + assert( pSource && inSize > 0 ); + assert( IsValid(format) && !IsVideo(format) ); + + switch( format ) + { + //--------------------------------------------------------------------------------- + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + if ( flags & TEXP_SCANLINE_LEGACY ) + { + // Swap Red (R) and Blue (B) channel (used for D3DFMT_A2R10G10B10 legacy sources) + if ( pDestination == pSource ) + { + uint32_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 4 ) + { + uint32_t t = *dPtr; + + uint32_t t1 = (t & 0x3ff00000) >> 20; + uint32_t t2 = (t & 0x000003ff) << 20; + uint32_t t3 = (t & 0x000ffc00); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xC0000000 : (t & 0xC0000000); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + else + { + const uint32_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 4 ) + { + uint32_t t = *(sPtr++); + + uint32_t t1 = (t & 0x3ff00000) >> 20; + uint32_t t2 = (t & 0x000003ff) << 20; + uint32_t t3 = (t & 0x000ffc00); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xC0000000 : (t & 0xC0000000); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + return; + } + break; + + //--------------------------------------------------------------------------------- + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + // Swap Red (R) and Blue (B) channels (used to convert from DXGI 1.1 BGR formats to DXGI 1.0 RGB) + if ( pDestination == pSource ) + { + uint32_t *dPtr = reinterpret_cast(pDestination); + for( size_t count = 0; count < outSize; count += 4 ) + { + uint32_t t = *dPtr; + + uint32_t t1 = (t & 0x00ff0000) >> 16; + uint32_t t2 = (t & 0x000000ff) << 16; + uint32_t t3 = (t & 0x0000ff00); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (t & 0xFF000000); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + else + { + const uint32_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + size_t size = std::min( outSize, inSize ); + for( size_t count = 0; count < size; count += 4 ) + { + uint32_t t = *(sPtr++); + + uint32_t t1 = (t & 0x00ff0000) >> 16; + uint32_t t2 = (t & 0x000000ff) << 16; + uint32_t t3 = (t & 0x0000ff00); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (t & 0xFF000000); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + return; + } + + // Fall-through case is to just use memcpy (assuming this is not an in-place operation) + if ( pDestination == pSource ) + return; + + size_t size = std::min( outSize, inSize ); + memcpy_s( pDestination, outSize, pSource, size ); +} + + +//------------------------------------------------------------------------------------- +// Converts an image row with optional clearing of alpha value to 1.0 +// Returns true if supported, false if expansion case not supported +//------------------------------------------------------------------------------------- +bool _ExpandScanline( LPVOID pDestination, size_t outSize, DXGI_FORMAT outFormat, + LPCVOID pSource, size_t inSize, DXGI_FORMAT inFormat, DWORD flags ) +{ + assert( pDestination && outSize > 0 ); + assert( pSource && inSize > 0 ); + assert( IsValid(outFormat) && !IsVideo(outFormat) ); + assert( IsValid(inFormat) && !IsVideo(inFormat) ); + + switch( inFormat ) + { + case DXGI_FORMAT_B5G6R5_UNORM: + if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM ) + return false; + + // DXGI_FORMAT_B5G6R5_UNORM -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 ) + { + uint16_t t = *(sPtr++); + + uint32_t t1 = ((t & 0xf800) >> 8) | ((t & 0xe000) >> 13); + uint32_t t2 = ((t & 0x07e0) << 5) | ((t & 0x0600) >> 5); + uint32_t t3 = ((t & 0x001f) << 19) | ((t & 0x001c) << 14); + + *(dPtr++) = t1 | t2 | t3 | 0xff000000; + } + } + return true; + + case DXGI_FORMAT_B5G5R5A1_UNORM: + if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM ) + return false; + + // DXGI_FORMAT_B5G5R5A1_UNORM -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 ) + { + uint16_t t = *(sPtr++); + + uint32_t t1 = ((t & 0x7c00) >> 7) | ((t & 0x7000) >> 12); + uint32_t t2 = ((t & 0x03e0) << 6) | ((t & 0x0380) << 1); + uint32_t t3 = ((t & 0x001f) << 19) | ((t & 0x001c) << 14); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0x8000) ? 0xff000000 : 0); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + return true; + +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: + if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM ) + return false; + + // DXGI_FORMAT_B4G4R4A4_UNORM -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 ) + { + uint16_t t = *(sPtr++); + + uint32_t t1 = ((t & 0x0f00) >> 4) | ((t & 0x0f00) >> 8); + uint32_t t2 = ((t & 0x00f0) << 8) | ((t & 0x00f0) << 4); + uint32_t t3 = ((t & 0x000f) << 20) | ((t & 0x000f) << 16); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf000) << 16) | ((t & 0xf000) << 12)); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + return true; +#endif // DXGI_1_2_FORMATS + } + + return false; +} + + +//------------------------------------------------------------------------------------- +// Loads an image row into standard RGBA XMVECTOR (aligned) array +//------------------------------------------------------------------------------------- +#define LOAD_SCANLINE( type, func )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < size; icount += sizeof(type) )\ + {\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = func( sPtr++ );\ + }\ + }\ + return true; + +#define LOAD_SCANLINE3( type, func, defvec )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < size; icount += sizeof(type) )\ + {\ + XMVECTOR v = func( sPtr++ );\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = XMVectorSelect( defvec, v, g_XMSelect1110 );\ + }\ + }\ + return true; + +#define LOAD_SCANLINE2( type, func, defvec )\ + if ( size >= sizeof(type) )\ + {\ + const type * __restrict sPtr = reinterpret_cast(pSource);\ + for( size_t icount = 0; icount < size; icount += sizeof(type) )\ + {\ + XMVECTOR v = func( sPtr++ );\ + if ( dPtr >= ePtr ) break;\ + *(dPtr++) = XMVectorSelect( defvec, v, g_XMSelect1100 );\ + }\ + }\ + return true; + +bool _LoadScanline( XMVECTOR* pDestination, size_t count, + LPCVOID pSource, size_t size, DXGI_FORMAT format ) +{ + assert( pDestination && count > 0 && (((uintptr_t)pDestination & 0xF) == 0) ); + assert( pSource && size > 0 ); + assert( IsValid(format) && !IsVideo(format) && !IsTypeless(format) && !IsCompressed(format) ); + + XMVECTOR* __restrict dPtr = pDestination; + if ( !dPtr ) + return false; + + const XMVECTOR* ePtr = pDestination + count; + + switch( format ) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + { + size_t msize = (size > (sizeof(XMVECTOR)*count)) ? (sizeof(XMVECTOR)*count) : size; + memcpy_s( dPtr, sizeof(XMVECTOR)*count, pSource, msize ); + } + return true; + + case DXGI_FORMAT_R32G32B32A32_UINT: + LOAD_SCANLINE( XMUINT4, XMLoadUInt4 ) + + case DXGI_FORMAT_R32G32B32A32_SINT: + LOAD_SCANLINE( XMINT4, XMLoadSInt4 ) + + case DXGI_FORMAT_R32G32B32_FLOAT: + LOAD_SCANLINE3( XMFLOAT3, XMLoadFloat3, g_XMIdentityR3 ) + + case DXGI_FORMAT_R32G32B32_UINT: + LOAD_SCANLINE3( XMUINT3, XMLoadUInt3, g_XMIdentityR3 ) + + case DXGI_FORMAT_R32G32B32_SINT: + LOAD_SCANLINE3( XMINT3, XMLoadSInt3, g_XMIdentityR3 ) + + case DXGI_FORMAT_R16G16B16A16_FLOAT: + LOAD_SCANLINE( XMHALF4, XMLoadHalf4 ) + + case DXGI_FORMAT_R16G16B16A16_UNORM: + LOAD_SCANLINE( XMUSHORTN4, XMLoadUShortN4 ) + + case DXGI_FORMAT_R16G16B16A16_UINT: + LOAD_SCANLINE( XMUSHORT4, XMLoadUShort4 ) + + case DXGI_FORMAT_R16G16B16A16_SNORM: + LOAD_SCANLINE( XMSHORTN4, XMLoadShortN4 ) + + case DXGI_FORMAT_R16G16B16A16_SINT: + LOAD_SCANLINE( XMSHORT4, XMLoadShort4 ) + + case DXGI_FORMAT_R32G32_FLOAT: + LOAD_SCANLINE2( XMFLOAT2, XMLoadFloat2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R32G32_UINT: + LOAD_SCANLINE2( XMUINT2, XMLoadUInt2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R32G32_SINT: + LOAD_SCANLINE2( XMINT2, XMLoadSInt2, g_XMIdentityR3 ) + + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + if ( size >= (sizeof(float)+sizeof(uint32_t)) ) + { + const float * sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += (sizeof(float)+sizeof(uint32_t)) ) + { + const uint8_t* ps8 = reinterpret_cast( &sPtr[1] ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( sPtr[0], static_cast( *ps8 ), 0.f, 1.f ); + sPtr += 2; + } + } + return true; + + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + LOAD_SCANLINE( XMUDECN4, XMLoadUDecN4 ); + + case DXGI_FORMAT_R10G10B10A2_UINT: + LOAD_SCANLINE( XMUDEC4, XMLoadUDec4 ); + + case DXGI_FORMAT_R11G11B10_FLOAT: + LOAD_SCANLINE3( XMFLOAT3PK, XMLoadFloat3PK, g_XMIdentityR3 ); + + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + LOAD_SCANLINE( XMUBYTEN4, XMLoadUByteN4 ) + + case DXGI_FORMAT_R8G8B8A8_UINT: + LOAD_SCANLINE( XMUBYTE4, XMLoadUByte4 ) + + case DXGI_FORMAT_R8G8B8A8_SNORM: + LOAD_SCANLINE( XMBYTEN4, XMLoadByteN4 ) + + case DXGI_FORMAT_R8G8B8A8_SINT: + LOAD_SCANLINE( XMBYTE4, XMLoadByte4 ) + + case DXGI_FORMAT_R16G16_FLOAT: + LOAD_SCANLINE2( XMHALF2, XMLoadHalf2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R16G16_UNORM: + LOAD_SCANLINE2( XMUSHORTN2, XMLoadUShortN2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R16G16_UINT: + LOAD_SCANLINE2( XMUSHORT2, XMLoadUShort2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R16G16_SNORM: + LOAD_SCANLINE2( XMSHORTN2, XMLoadShortN2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R16G16_SINT: + LOAD_SCANLINE2( XMSHORT2, XMLoadShort2, g_XMIdentityR3 ) + + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + if ( size >= sizeof(float) ) + { + const float* __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(float) ) + { + XMVECTOR v = XMLoadFloat( sPtr++ ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v, g_XMSelect1000 ); + } + } + return true; + + case DXGI_FORMAT_R32_UINT: + if ( size >= sizeof(uint32_t) ) + { + const uint32_t* __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint32_t) ) + { + XMVECTOR v = XMLoadInt( sPtr++ ); + v = XMConvertVectorUIntToFloat( v, 0 ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v, g_XMSelect1000 ); + } + } + return true; + + case DXGI_FORMAT_R32_SINT: + if ( size >= sizeof(int32_t) ) + { + const int32_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(int32_t) ) + { + XMVECTOR v = XMLoadInt( reinterpret_cast (sPtr++) ); + v = XMConvertVectorIntToFloat( v, 0 ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v, g_XMSelect1000 ); + } + } + return true; + + case DXGI_FORMAT_D24_UNORM_S8_UINT: + if ( size >= sizeof(uint32_t) ) + { + const uint32_t * sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint32_t) ) + { + float d = static_cast( *sPtr & 0xFFFFFF ) / 16777215.f; + float s = static_cast( ( *sPtr & 0xFF000000 ) >> 24 ); + ++sPtr; + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( d, s, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R8G8_UNORM: + LOAD_SCANLINE2( XMUBYTEN2, XMLoadUByteN2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R8G8_UINT: + LOAD_SCANLINE2( XMUBYTE2, XMLoadUByte2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R8G8_SNORM: + LOAD_SCANLINE2( XMBYTEN2, XMLoadByteN2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R8G8_SINT: + LOAD_SCANLINE2( XMBYTE2, XMLoadByte2, g_XMIdentityR3 ) + + case DXGI_FORMAT_R16_FLOAT: + if ( size >= sizeof(HALF) ) + { + const HALF * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(HALF) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( XMConvertHalfToFloat(*sPtr++), 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + if ( size >= sizeof(uint16_t) ) + { + const uint16_t* __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint16_t) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++) / 65535.f, 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R16_UINT: + if ( size >= sizeof(uint16_t) ) + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint16_t) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++), 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R16_SNORM: + if ( size >= sizeof(int16_t) ) + { + const int16_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(int16_t) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++) / 32767.f, 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R16_SINT: + if ( size >= sizeof(int16_t) ) + { + const int16_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(int16_t) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++), 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R8_UNORM: + if ( size >= sizeof(uint8_t) ) + { + const uint8_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++) / 255.f, 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R8_UINT: + if ( size >= sizeof(uint8_t) ) + { + const uint8_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++), 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R8_SNORM: + if ( size >= sizeof(char) ) + { + const char * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(char) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++) / 127.f, 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_R8_SINT: + if ( size >= sizeof(char) ) + { + const char * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(char) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( static_cast(*sPtr++), 0.f, 0.f, 1.f ); + } + } + return true; + + case DXGI_FORMAT_A8_UNORM: + if ( size >= sizeof(uint8_t) ) + { + const uint8_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( 0.f, 0.f, 0.f, static_cast(*sPtr++) / 255.f ); + } + } + return true; + + case DXGI_FORMAT_R1_UNORM: + if ( size >= sizeof(uint8_t) ) + { + const uint8_t * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + for( size_t bcount = 0; bcount < 8; ++bcount ) + { + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSet( (((*sPtr >> bcount) & 0x1) ? 1.f : 0.f), 0.f, 0.f, 1.f ); + } + + ++sPtr; + } + } + return true; + + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + LOAD_SCANLINE3( XMFLOAT3SE, XMLoadFloat3SE, g_XMIdentityR3 ) + + case DXGI_FORMAT_R8G8_B8G8_UNORM: + if ( size >= sizeof(XMUBYTEN4) ) + { + const XMUBYTEN4 * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + XMVECTOR v = XMLoadUByteN4( sPtr++ ); + XMVECTOR v1 = XMVectorSwizzle<0, 3, 2, 1>( v ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v, g_XMSelect1110 ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v1, g_XMSelect1110 ); + } + } + return true; + + case DXGI_FORMAT_G8R8_G8B8_UNORM: + if ( size >= sizeof(XMUBYTEN4) ) + { + const XMUBYTEN4 * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + XMVECTOR v = XMLoadUByteN4( sPtr++ ); + XMVECTOR v0 = XMVectorSwizzle<1, 0, 3, 2>( v ); + XMVECTOR v1 = XMVectorSwizzle<1, 2, 3, 0>( v ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v0, g_XMSelect1110 ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v1, g_XMSelect1110 ); + } + } + return true; + + case DXGI_FORMAT_B5G6R5_UNORM: + if ( size >= sizeof(XMU565) ) + { + static XMVECTORF32 s_Scale = { 1.f/31.f, 1.f/63.f, 1.f/31.f, 1.f }; + const XMU565 * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(XMU565) ) + { + XMVECTOR v = XMLoadU565( sPtr++ ); + v = XMVectorMultiply( v, s_Scale ); + v = XMVectorSwizzle<2, 1, 0, 3>( v ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v, g_XMSelect1110 ); + } + } + return true; + + case DXGI_FORMAT_B5G5R5A1_UNORM: + if ( size >= sizeof(XMU555) ) + { + static XMVECTORF32 s_Scale = { 1.f/31.f, 1.f/31.f, 1.f/31.f, 1.f }; + const XMU555 * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(XMU555) ) + { + XMVECTOR v = XMLoadU555( sPtr++ ); + v = XMVectorMultiply( v, s_Scale ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSwizzle<2, 1, 0, 3>( v ); + } + } + return true; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + if ( size >= sizeof(XMUBYTEN4) ) + { + const XMUBYTEN4 * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + XMVECTOR v = XMLoadUByteN4( sPtr++ ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSwizzle<2, 1, 0, 3>( v ); + } + } + return true; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + if ( size >= sizeof(XMUBYTEN4) ) + { + const XMUBYTEN4 * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + XMVECTOR v = XMLoadUByteN4( sPtr++ ); + v = XMVectorSwizzle<2, 1, 0, 3>( v ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSelect( g_XMIdentityR3, v, g_XMSelect1110 ); + } + } + return true; + +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: + if ( size >= sizeof(XMUNIBBLE4) ) + { + static XMVECTORF32 s_Scale = { 1.f/15.f, 1.f/15.f, 1.f/15.f, 1.f/15.f }; + const XMUNIBBLE4 * __restrict sPtr = reinterpret_cast(pSource); + for( size_t icount = 0; icount < size; icount += sizeof(XMUNIBBLE4) ) + { + XMVECTOR v = XMLoadUNibble4( sPtr++ ); + v = XMVectorMultiply( v, s_Scale ); + if ( dPtr >= ePtr ) break; + *(dPtr++) = XMVectorSwizzle<2, 1, 0, 3>( v ); + } + } + return true; + + // we don't support the video formats ( see IsVideo function ) +#endif // DXGI_1_2_FORMATS + + default: + return false; + } +} + + +//------------------------------------------------------------------------------------- +// Stores an image row from standard RGBA XMVECTOR (aligned) array +//------------------------------------------------------------------------------------- +#define STORE_SCANLINE( type, func )\ + if ( size >= sizeof(type) )\ + {\ + type * __restrict dPtr = reinterpret_cast(pDestination);\ + for( size_t icount = 0; icount < size; icount += sizeof(type) )\ + {\ + if ( sPtr >= ePtr ) break;\ + func( dPtr++, *sPtr++ );\ + }\ + }\ + return true; + +bool _StoreScanline( LPVOID pDestination, size_t size, DXGI_FORMAT format, + const XMVECTOR* pSource, size_t count ) +{ + assert( pDestination && size > 0 ); + assert( pSource && count > 0 && (((uintptr_t)pSource & 0xF) == 0) ); + assert( IsValid(format) && !IsVideo(format) && !IsTypeless(format) && !IsCompressed(format) ); + + const XMVECTOR* __restrict sPtr = pSource; + if ( !sPtr ) + return false; + + const XMVECTOR* ePtr = pSource + count; + + switch( format ) + { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + STORE_SCANLINE( XMFLOAT4, XMStoreFloat4 ) + + case DXGI_FORMAT_R32G32B32A32_UINT: + STORE_SCANLINE( XMUINT4, XMStoreUInt4 ) + + case DXGI_FORMAT_R32G32B32A32_SINT: + STORE_SCANLINE( XMINT4, XMStoreSInt4 ) + + case DXGI_FORMAT_R32G32B32_FLOAT: + STORE_SCANLINE( XMFLOAT3, XMStoreFloat3 ) + + case DXGI_FORMAT_R32G32B32_UINT: + STORE_SCANLINE( XMUINT3, XMStoreUInt3 ) + + case DXGI_FORMAT_R32G32B32_SINT: + STORE_SCANLINE( XMINT3, XMStoreSInt3 ) + + case DXGI_FORMAT_R16G16B16A16_FLOAT: + STORE_SCANLINE( XMHALF4, XMStoreHalf4 ) + + case DXGI_FORMAT_R16G16B16A16_UNORM: + STORE_SCANLINE( XMUSHORTN4, XMStoreUShortN4 ) + + case DXGI_FORMAT_R16G16B16A16_UINT: + STORE_SCANLINE( XMUSHORT4, XMStoreUShort4 ) + + case DXGI_FORMAT_R16G16B16A16_SNORM: + STORE_SCANLINE( XMSHORTN4, XMStoreShortN4 ) + + case DXGI_FORMAT_R16G16B16A16_SINT: + STORE_SCANLINE( XMSHORT4, XMStoreShort4 ) + + case DXGI_FORMAT_R32G32_FLOAT: + STORE_SCANLINE( XMFLOAT2, XMStoreFloat2 ) + + case DXGI_FORMAT_R32G32_UINT: + STORE_SCANLINE( XMUINT2, XMStoreUInt2 ) + + case DXGI_FORMAT_R32G32_SINT: + STORE_SCANLINE( XMINT2, XMStoreSInt2 ) + + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + if ( size >= (sizeof(float)+sizeof(uint32_t)) ) + { + float *dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += (sizeof(float)+sizeof(uint32_t)) ) + { + if ( sPtr >= ePtr ) break; + XMFLOAT4 f; + XMStoreFloat4( &f, *sPtr++ ); + dPtr[0] = f.x; + uint8_t* ps8 = reinterpret_cast( &dPtr[1] ); + ps8[0] = static_cast( std::min( 255.f, std::max( 0.f, f.y ) ) ); + ps8[1] = ps8[2] = ps8[3] = 0; + dPtr += 2; + } + } + return true; + + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + STORE_SCANLINE( XMUDECN4, XMStoreUDecN4 ); + + case DXGI_FORMAT_R10G10B10A2_UINT: + STORE_SCANLINE( XMUDEC4, XMStoreUDec4 ); + + case DXGI_FORMAT_R11G11B10_FLOAT: + STORE_SCANLINE( XMFLOAT3PK, XMStoreFloat3PK ); + + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + STORE_SCANLINE( XMUBYTEN4, XMStoreUByteN4 ) + + case DXGI_FORMAT_R8G8B8A8_UINT: + STORE_SCANLINE( XMUBYTE4, XMStoreUByte4 ) + + case DXGI_FORMAT_R8G8B8A8_SNORM: + STORE_SCANLINE( XMBYTEN4, XMStoreByteN4 ) + + case DXGI_FORMAT_R8G8B8A8_SINT: + STORE_SCANLINE( XMBYTE4, XMStoreByte4 ) + + case DXGI_FORMAT_R16G16_FLOAT: + STORE_SCANLINE( XMHALF2, XMStoreHalf2 ) + + case DXGI_FORMAT_R16G16_UNORM: + STORE_SCANLINE( XMUSHORTN2, XMStoreUShortN2 ) + + case DXGI_FORMAT_R16G16_UINT: + STORE_SCANLINE( XMUSHORT2, XMStoreUShort2 ) + + case DXGI_FORMAT_R16G16_SNORM: + STORE_SCANLINE( XMSHORTN2, XMStoreShortN2 ) + + case DXGI_FORMAT_R16G16_SINT: + STORE_SCANLINE( XMSHORT2, XMStoreShort2 ) + + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + if ( size >= sizeof(float) ) + { + float * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(float) ) + { + if ( sPtr >= ePtr ) break; + XMStoreFloat( dPtr++, *(sPtr++) ); + } + } + return true; + + case DXGI_FORMAT_R32_UINT: + if ( size >= sizeof(uint32_t) ) + { + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint32_t) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v = XMConvertVectorFloatToUInt( *(sPtr++), 0 ); + XMStoreInt( dPtr++, v ); + } + } + return true; + + case DXGI_FORMAT_R32_SINT: + if ( size >= sizeof(uint32_t) ) + { + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint32_t) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v = XMConvertVectorFloatToInt( *(sPtr++), 0 ); + XMStoreInt( dPtr++, v ); + } + } + return true; + + case DXGI_FORMAT_D24_UNORM_S8_UINT: + if ( size >= sizeof(uint32_t) ) + { + static const XMVECTORF32 clamp = { 1.f, 255.f, 0.f, 0.f }; + XMVECTOR zero = XMVectorZero(); + uint32_t *dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint32_t) ) + { + if ( sPtr >= ePtr ) break; + XMFLOAT4 f; + XMStoreFloat4( &f, XMVectorClamp( *sPtr++, zero, clamp ) ); + *dPtr++ = (static_cast( f.x * 16777215.f ) & 0xFFFFFF) + | ((static_cast( f.y ) & 0xFF) << 24); + } + } + return true; + + case DXGI_FORMAT_R8G8_UNORM: + STORE_SCANLINE( XMUBYTEN2, XMStoreUByteN2 ) + + case DXGI_FORMAT_R8G8_UINT: + STORE_SCANLINE( XMUBYTE2, XMStoreUByte2 ) + + case DXGI_FORMAT_R8G8_SNORM: + STORE_SCANLINE( XMBYTEN2, XMStoreByteN2 ) + + case DXGI_FORMAT_R8G8_SINT: + STORE_SCANLINE( XMBYTE2, XMStoreByte2 ) + + case DXGI_FORMAT_R16_FLOAT: + if ( size >= sizeof(HALF) ) + { + HALF * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(HALF) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + *(dPtr++) = XMConvertFloatToHalf(v); + } + } + return true; + + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + if ( size >= sizeof(int16_t) ) + { + int16_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(int16_t) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 1.f ), 0.f ); + *(dPtr++) = static_cast( v*65535.f + 0.5f ); + } + } + return true; + + case DXGI_FORMAT_R16_UINT: + if ( size >= sizeof(uint16_t) ) + { + uint16_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint16_t) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 65535.f ), 0.f ); + *(dPtr++) = static_cast(v); + } + } + return true; + + case DXGI_FORMAT_R16_SNORM: + if ( size >= sizeof(int16_t) ) + { + int16_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(int16_t) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 1.f ), -1.f ); + *(dPtr++) = static_cast( v * 32767.f ); + } + } + return true; + + case DXGI_FORMAT_R16_SINT: + if ( size >= sizeof(int16_t) ) + { + int16_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(int16_t) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 32767.f ), -32767.f ); + *(dPtr++) = static_cast(v); + } + } + return true; + + case DXGI_FORMAT_R8_UNORM: + if ( size >= sizeof(uint8_t) ) + { + uint8_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 1.f ), 0.f ); + *(dPtr++) = static_cast( v * 255.f); + } + } + return true; + + case DXGI_FORMAT_R8_UINT: + if ( size >= sizeof(uint8_t) ) + { + uint8_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 255.f ), 0.f ); + *(dPtr++) = static_cast(v); + } + } + return true; + + case DXGI_FORMAT_R8_SNORM: + if ( size >= sizeof(char) ) + { + char * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(char) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 1.f ), -1.f ); + *(dPtr++) = static_cast( v * 127.f ); + } + } + return true; + + case DXGI_FORMAT_R8_SINT: + if ( size >= sizeof(char) ) + { + char * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(char) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + v = std::max( std::min( v, 127.f ), -127.f ); + *(dPtr++) = static_cast( v ); + } + } + return true; + + case DXGI_FORMAT_A8_UNORM: + if ( size >= sizeof(uint8_t) ) + { + uint8_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetW( *sPtr++ ); + v = std::max( std::min( v, 1.f ), 0.f ); + *(dPtr++) = static_cast( v * 255.f); + } + } + return true; + + case DXGI_FORMAT_R1_UNORM: + if ( size >= sizeof(uint8_t) ) + { + uint8_t * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(uint8_t) ) + { + uint8_t pixels = 0; + for( size_t bcount = 0; bcount < 8; ++bcount ) + { + if ( sPtr >= ePtr ) break; + float v = XMVectorGetX( *sPtr++ ); + if ( v > 0.5f ) + pixels |= 1 << bcount; + } + *(dPtr++) = pixels; + } + } + return true; + + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + STORE_SCANLINE( XMFLOAT3SE, XMStoreFloat3SE ) + + case DXGI_FORMAT_R8G8_B8G8_UNORM: + if ( size >= sizeof(XMUBYTEN4) ) + { + XMUBYTEN4 * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v0 = *sPtr++; + XMVECTOR v1 = (sPtr < ePtr) ? XMVectorSplatY( *sPtr++ ) : XMVectorZero(); + XMVECTOR v = XMVectorSelect( v1, v0, g_XMSelect1110 ); + XMStoreUByteN4( dPtr++, v ); + } + } + return true; + + case DXGI_FORMAT_G8R8_G8B8_UNORM: + if ( size >= sizeof(XMUBYTEN4) ) + { + static XMVECTORI32 select1101 = {XM_SELECT_1, XM_SELECT_1, XM_SELECT_0, XM_SELECT_1}; + + XMUBYTEN4 * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v0 = XMVectorSwizzle<1, 0, 3, 2>( *sPtr++ ); + XMVECTOR v1 = (sPtr < ePtr) ? XMVectorSplatY( *sPtr++ ) : XMVectorZero(); + XMVECTOR v = XMVectorSelect( v1, v0, select1101 ); + XMStoreUByteN4( dPtr++, v ); + } + } + return true; + + case DXGI_FORMAT_B5G6R5_UNORM: + if ( size >= sizeof(XMU565) ) + { + static XMVECTORF32 s_Scale = { 31.f, 63.f, 31.f, 1.f }; + XMU565 * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(XMU565) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v = XMVectorSwizzle<2, 1, 0, 3>( *sPtr++ ); + v = XMVectorMultiply( v, s_Scale ); + XMStoreU565( dPtr++, v ); + } + } + return true; + + case DXGI_FORMAT_B5G5R5A1_UNORM: + if ( size >= sizeof(XMU555) ) + { + static XMVECTORF32 s_Scale = { 31.f, 31.f, 31.f, 1.f }; + XMU555 * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(XMU555) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v = XMVectorSwizzle<2, 1, 0, 3>( *sPtr++ ); + v = XMVectorMultiply( v, s_Scale ); + XMStoreU555( dPtr++, v ); + } + } + return true; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + if ( size >= sizeof(XMUBYTEN4) ) + { + XMUBYTEN4 * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v = XMVectorSwizzle<2, 1, 0, 3>( *sPtr++ ); + XMStoreUByteN4( dPtr++, v ); + } + } + return true; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + if ( size >= sizeof(XMUBYTEN4) ) + { + XMUBYTEN4 * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(XMUBYTEN4) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v = XMVectorPermute( *sPtr++, g_XMIdentityR3 ); + XMStoreUByteN4( dPtr++, v ); + } + } + return true; + +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: + if ( size >= sizeof(XMUNIBBLE4) ) + { + static XMVECTORF32 s_Scale = { 15.f, 15.f, 15.f, 15.f }; + XMUNIBBLE4 * __restrict dPtr = reinterpret_cast(pDestination); + for( size_t icount = 0; icount < size; icount += sizeof(XMUNIBBLE4) ) + { + if ( sPtr >= ePtr ) break; + XMVECTOR v = XMVectorSwizzle<2, 1, 0, 3>( *sPtr++ ); + v = XMVectorMultiply( v, s_Scale ); + XMStoreUNibble4( dPtr++, v ); + } + } + return true; + + // We don't support the video formats ( see IsVideo function ) +#endif // DXGI_1_2_FORMATS + + default: + return false; + } +} + + +//------------------------------------------------------------------------------------- +// Convert DXGI image to/from GUID_WICPixelFormat128bppRGBAFloat (no range conversions) +//------------------------------------------------------------------------------------- +HRESULT _ConvertToR32G32B32A32( const Image& srcImage, ScratchImage& image ) +{ + if ( !srcImage.pixels ) + return E_POINTER; + + HRESULT hr = image.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.width, srcImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *img = image.GetImage( 0, 0, 0 ); + if ( !img ) + { + image.Release(); + return E_POINTER; + } + + uint8_t* pDest = img->pixels; + if ( !pDest ) + { + image.Release(); + return E_POINTER; + } + + const uint8_t *pSrc = srcImage.pixels; + for( size_t h = 0; h < srcImage.height; ++h ) + { + if ( !_LoadScanline( reinterpret_cast(pDest), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format ) ) + { + image.Release(); + return E_FAIL; + } + + pSrc += srcImage.rowPitch; + pDest += img->rowPitch; + } + + return S_OK; +} + +HRESULT _ConvertFromR32G32B32A32( _In_ const Image& srcImage, _In_ const Image& destImage ) +{ + assert( srcImage.format == DXGI_FORMAT_R32G32B32A32_FLOAT ); + + if ( !srcImage.pixels || !destImage.pixels ) + return E_POINTER; + + if ( srcImage.width != destImage.width || srcImage.height != destImage.height ) + return E_FAIL; + + const uint8_t *pSrc = srcImage.pixels; + uint8_t* pDest = destImage.pixels; + + for( size_t h = 0; h < srcImage.height; ++h ) + { + if ( !_StoreScanline( pDest, destImage.rowPitch, destImage.format, reinterpret_cast(pSrc), srcImage.width ) ) + return E_FAIL; + + pSrc += srcImage.rowPitch; + pDest += destImage.rowPitch; + } + + return S_OK; +} + +HRESULT _ConvertFromR32G32B32A32( const Image& srcImage, DXGI_FORMAT format, ScratchImage& image ) +{ + if ( !srcImage.pixels ) + return E_POINTER; + + HRESULT hr = image.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *img = image.GetImage( 0, 0, 0 ); + if ( !img ) + { + image.Release(); + return E_POINTER; + } + + hr = _ConvertFromR32G32B32A32( srcImage, *img ); + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + return S_OK; +} + +HRESULT _ConvertFromR32G32B32A32( const Image* srcImages, size_t nimages, const TexMetadata& metadata, DXGI_FORMAT format, ScratchImage& result ) +{ + if ( !srcImages ) + return E_POINTER; + + result.Release(); + + assert( metadata.format == DXGI_FORMAT_R32G32B32A32_FLOAT ); + + TexMetadata mdata2 = metadata; + mdata2.format = format; + HRESULT hr = result.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + if ( nimages != result.GetImageCount() ) + { + result.Release(); + return E_FAIL; + } + + const Image* dest = result.GetImages(); + if ( !dest ) + { + result.Release(); + return E_POINTER; + } + + for( size_t index=0; index < nimages; ++index ) + { + const Image& src = srcImages[ index ]; + const Image& dst = dest[ index ]; + + assert( src.format == DXGI_FORMAT_R32G32B32A32_FLOAT ); + assert( dst.format == format ); + + if ( src.width != dst.width || src.height != dst.height ) + { + result.Release(); + return E_FAIL; + } + + const uint8_t* pSrc = src.pixels; + uint8_t* pDest = dst.pixels; + if ( !pSrc || !pDest ) + { + result.Release(); + return E_POINTER; + } + + for( size_t h=0; h < src.height; ++h ) + { + if ( !_StoreScanline( pDest, dst.rowPitch, format, reinterpret_cast(pSrc), src.width ) ) + { + result.Release(); + return E_FAIL; + } + + pSrc += src.rowPitch; + pDest += dst.rowPitch; + } + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// RGB -> sRGB +//------------------------------------------------------------------------------------- +static const uint32_t g_fEncodeGamma22[] = +{ + 0x00000000, 0x3bd56bd3, 0x3c486344, 0x3c90da15, 0x3cbc2677, 0x3ce67704, 0x3d080183, 0x3d1c7728, + 0x3d30a8fb, 0x3d44a03c, 0x3d586400, 0x3d6bf9e7, 0x3d7f6679, 0x3d8956bd, 0x3d92e906, 0x3d9c6b70, + 0x3da5df22, 0x3daf451b, 0x3db89e3e, 0x3dc1eb50, 0x3dcb2d04, 0x3dd463f7, 0x3ddd90b9, 0x3de6b3ca, + 0x3defcda0, 0x3df8dea6, 0x3e00f3a0, 0x3e0573e3, 0x3e09f046, 0x3e0e68f0, 0x3e12de06, 0x3e174fa6, + 0x3e1bbdf2, 0x3e202906, 0x3e2490fd, 0x3e28f5f1, 0x3e2d57fb, 0x3e31b72f, 0x3e3613a4, 0x3e3a6d6e, + 0x3e3ec4a0, 0x3e43194d, 0x3e476b84, 0x3e4bbb57, 0x3e5008d7, 0x3e54540f, 0x3e589d0f, 0x3e5ce3e5, + 0x3e61289d, 0x3e656b44, 0x3e69abe5, 0x3e6dea8d, 0x3e722745, 0x3e766217, 0x3e7a9b0e, 0x3e7ed235, + 0x3e8183c9, 0x3e839d98, 0x3e85b68c, 0x3e87cea8, 0x3e89e5f2, 0x3e8bfc6b, 0x3e8e1219, 0x3e9026ff, + 0x3e923b20, 0x3e944e7f, 0x3e966120, 0x3e987307, 0x3e9a8436, 0x3e9c94af, 0x3e9ea476, 0x3ea0b38e, + 0x3ea2c1fb, 0x3ea4cfbb, 0x3ea6dcd5, 0x3ea8e94a, 0x3eaaf51c, 0x3ead004e, 0x3eaf0ae2, 0x3eb114d9, + 0x3eb31e37, 0x3eb526fe, 0x3eb72f2f, 0x3eb936cd, 0x3ebb3dd8, 0x3ebd4454, 0x3ebf4a43, 0x3ec14fa5, + 0x3ec3547e, 0x3ec558cd, 0x3ec75c95, 0x3ec95fd8, 0x3ecb6297, 0x3ecd64d4, 0x3ecf6690, 0x3ed167ce, + 0x3ed3688e, 0x3ed568d1, 0x3ed76899, 0x3ed967e9, 0x3edb66bf, 0x3edd651f, 0x3edf630a, 0x3ee16080, + 0x3ee35d84, 0x3ee55a16, 0x3ee75636, 0x3ee951e8, 0x3eeb4d2a, 0x3eed4800, 0x3eef4269, 0x3ef13c68, + 0x3ef335fc, 0x3ef52f26, 0x3ef727ea, 0x3ef92046, 0x3efb183c, 0x3efd0fcd, 0x3eff06fa, 0x3f007ee2, + 0x3f017a16, 0x3f027519, 0x3f036fec, 0x3f046a8f, 0x3f056502, 0x3f065f47, 0x3f07595d, 0x3f085344, + 0x3f094cfe, 0x3f0a468b, 0x3f0b3feb, 0x3f0c391e, 0x3f0d3224, 0x3f0e2aff, 0x3f0f23af, 0x3f101c32, + 0x3f11148c, 0x3f120cba, 0x3f1304bf, 0x3f13fc9a, 0x3f14f44b, 0x3f15ebd3, 0x3f16e333, 0x3f17da6b, + 0x3f18d17a, 0x3f19c860, 0x3f1abf1f, 0x3f1bb5b7, 0x3f1cac28, 0x3f1da272, 0x3f1e9895, 0x3f1f8e92, + 0x3f20846a, 0x3f217a1c, 0x3f226fa8, 0x3f23650f, 0x3f245a52, 0x3f254f70, 0x3f264469, 0x3f27393f, + 0x3f282df1, 0x3f29227f, 0x3f2a16ea, 0x3f2b0b31, 0x3f2bff56, 0x3f2cf358, 0x3f2de738, 0x3f2edaf6, + 0x3f2fce91, 0x3f30c20b, 0x3f31b564, 0x3f32a89b, 0x3f339bb1, 0x3f348ea6, 0x3f35817a, 0x3f36742f, + 0x3f3766c3, 0x3f385936, 0x3f394b8a, 0x3f3a3dbe, 0x3f3b2fd3, 0x3f3c21c8, 0x3f3d139e, 0x3f3e0556, + 0x3f3ef6ee, 0x3f3fe868, 0x3f40d9c4, 0x3f41cb01, 0x3f42bc20, 0x3f43ad22, 0x3f449e06, 0x3f458ecc, + 0x3f467f75, 0x3f477001, 0x3f486071, 0x3f4950c2, 0x3f4a40f8, 0x3f4b3111, 0x3f4c210d, 0x3f4d10ed, + 0x3f4e00b2, 0x3f4ef05a, 0x3f4fdfe7, 0x3f50cf58, 0x3f51beae, 0x3f52ade8, 0x3f539d07, 0x3f548c0c, + 0x3f557af5, 0x3f5669c4, 0x3f575878, 0x3f584711, 0x3f593590, 0x3f5a23f6, 0x3f5b1241, 0x3f5c0072, + 0x3f5cee89, 0x3f5ddc87, 0x3f5eca6b, 0x3f5fb835, 0x3f60a5e7, 0x3f619380, 0x3f6280ff, 0x3f636e65, + 0x3f645bb3, 0x3f6548e8, 0x3f663604, 0x3f672309, 0x3f680ff4, 0x3f68fcc8, 0x3f69e983, 0x3f6ad627, + 0x3f6bc2b3, 0x3f6caf27, 0x3f6d9b83, 0x3f6e87c8, 0x3f6f73f5, 0x3f70600c, 0x3f714c0b, 0x3f7237f4, + 0x3f7323c4, 0x3f740f7f, 0x3f74fb22, 0x3f75e6af, 0x3f76d225, 0x3f77bd85, 0x3f78a8ce, 0x3f799401, + 0x3f7a7f1e, 0x3f7b6a25, 0x3f7c5516, 0x3f7d3ff1, 0x3f7e2ab6, 0x3f7f1566, 0x3f800000, 0x3f800000 +}; + +#pragma prefast(suppress : 25000, "FXMVECTOR is 16 bytes") +static inline XMVECTOR _TableEncodeGamma22( FXMVECTOR v ) +{ + float f[4]; + XMStoreFloat4( (XMFLOAT4*)f, v ); + + for( size_t i=0; i < 4; ++i ) + { + float f2 = sqrtf(f[i]) * 254.0f; + + uint32_t i2 = static_cast(f2); + i2 = std::min( i2, _countof( g_fEncodeGamma22 )-2 ); + + float fS = f2 - (float) i2; + float fA = ((float *) g_fEncodeGamma22)[i2]; + float fB = ((float *) g_fEncodeGamma22)[i2 + 1]; + + f[i] = fA + fS * (fB - fA); + } + + return XMLoadFloat4( (XMFLOAT4*)f ); +} + + +//------------------------------------------------------------------------------------- +// sRGB -> RGB +//------------------------------------------------------------------------------------- +static const uint32_t g_fDecodeGamma22[] = +{ + 0x00000000, 0x3b144eb0, 0x3b9ef3b0, 0x3bf84b42, 0x3c2a5c46, 0x3c59c180, 0x3c850eb5, 0x3c9da52a, + 0x3cb6967a, 0x3ccfd852, 0x3ce9628b, 0x3d01974b, 0x3d0e9b82, 0x3d1bbba3, 0x3d28f5bc, 0x3d364822, + 0x3d43b159, 0x3d51301d, 0x3d5ec344, 0x3d6c69c9, 0x3d7a22c4, 0x3d83f6ad, 0x3d8ae465, 0x3d91da35, + 0x3d98d7c7, 0x3d9fdcd2, 0x3da6e914, 0x3dadfc47, 0x3db51635, 0x3dbc36a3, 0x3dc35d62, 0x3dca8a3a, + 0x3dd1bd02, 0x3dd8f591, 0x3de033bb, 0x3de7775d, 0x3deec050, 0x3df60e74, 0x3dfd61a6, 0x3e025ce5, + 0x3e060b61, 0x3e09bc38, 0x3e0d6f5f, 0x3e1124c8, 0x3e14dc68, 0x3e189630, 0x3e1c521a, 0x3e201016, + 0x3e23d01d, 0x3e279225, 0x3e2b5624, 0x3e2f1c10, 0x3e32e3e4, 0x3e36ad94, 0x3e3a7918, 0x3e3e4668, + 0x3e42157f, 0x3e45e654, 0x3e49b8e0, 0x3e4d8d1d, 0x3e516304, 0x3e553a8d, 0x3e5913b4, 0x3e5cee70, + 0x3e60cabf, 0x3e64a89b, 0x3e6887fb, 0x3e6c68db, 0x3e704b3a, 0x3e742f0e, 0x3e781454, 0x3e7bfb04, + 0x3e7fe321, 0x3e81e650, 0x3e83dbc0, 0x3e85d1dc, 0x3e87c8a3, 0x3e89c015, 0x3e8bb830, 0x3e8db0ee, + 0x3e8faa51, 0x3e91a454, 0x3e939ef9, 0x3e959a3b, 0x3e97961b, 0x3e999295, 0x3e9b8fa7, 0x3e9d8d52, + 0x3e9f8b93, 0x3ea18a6a, 0x3ea389d2, 0x3ea589cb, 0x3ea78a56, 0x3ea98b6e, 0x3eab8d15, 0x3ead8f47, + 0x3eaf9204, 0x3eb1954a, 0x3eb39917, 0x3eb59d6c, 0x3eb7a246, 0x3eb9a7a5, 0x3ebbad88, 0x3ebdb3ec, + 0x3ebfbad3, 0x3ec1c237, 0x3ec3ca1a, 0x3ec5d27c, 0x3ec7db58, 0x3ec9e4b4, 0x3ecbee85, 0x3ecdf8d3, + 0x3ed0039a, 0x3ed20ed8, 0x3ed41a8a, 0x3ed626b5, 0x3ed83351, 0x3eda4065, 0x3edc4de9, 0x3ede5be0, + 0x3ee06a4a, 0x3ee27923, 0x3ee4886a, 0x3ee69821, 0x3ee8a845, 0x3eeab8d8, 0x3eecc9d6, 0x3eeedb3f, + 0x3ef0ed13, 0x3ef2ff53, 0x3ef511fb, 0x3ef7250a, 0x3ef93883, 0x3efb4c61, 0x3efd60a7, 0x3eff7553, + 0x3f00c531, 0x3f01cfeb, 0x3f02dad9, 0x3f03e5f5, 0x3f04f145, 0x3f05fcc4, 0x3f070875, 0x3f081456, + 0x3f092067, 0x3f0a2ca8, 0x3f0b3917, 0x3f0c45b7, 0x3f0d5284, 0x3f0e5f7f, 0x3f0f6caa, 0x3f107a03, + 0x3f118789, 0x3f12953b, 0x3f13a31d, 0x3f14b12b, 0x3f15bf64, 0x3f16cdca, 0x3f17dc5e, 0x3f18eb1b, + 0x3f19fa05, 0x3f1b091b, 0x3f1c185c, 0x3f1d27c7, 0x3f1e375c, 0x3f1f471d, 0x3f205707, 0x3f21671b, + 0x3f227759, 0x3f2387c2, 0x3f249852, 0x3f25a90c, 0x3f26b9ef, 0x3f27cafb, 0x3f28dc30, 0x3f29ed8b, + 0x3f2aff11, 0x3f2c10bd, 0x3f2d2290, 0x3f2e348b, 0x3f2f46ad, 0x3f3058f7, 0x3f316b66, 0x3f327dfd, + 0x3f3390ba, 0x3f34a39d, 0x3f35b6a7, 0x3f36c9d6, 0x3f37dd2b, 0x3f38f0a5, 0x3f3a0443, 0x3f3b1808, + 0x3f3c2bf2, 0x3f3d4000, 0x3f3e5434, 0x3f3f688c, 0x3f407d07, 0x3f4191a8, 0x3f42a66c, 0x3f43bb54, + 0x3f44d05f, 0x3f45e58e, 0x3f46fadf, 0x3f481054, 0x3f4925ed, 0x3f4a3ba8, 0x3f4b5186, 0x3f4c6789, + 0x3f4d7daa, 0x3f4e93f0, 0x3f4faa57, 0x3f50c0e0, 0x3f51d78b, 0x3f52ee58, 0x3f540545, 0x3f551c55, + 0x3f563386, 0x3f574ad7, 0x3f58624b, 0x3f5979de, 0x3f5a9191, 0x3f5ba965, 0x3f5cc15b, 0x3f5dd971, + 0x3f5ef1a6, 0x3f6009fc, 0x3f612272, 0x3f623b08, 0x3f6353bc, 0x3f646c90, 0x3f658586, 0x3f669e98, + 0x3f67b7cb, 0x3f68d11b, 0x3f69ea8d, 0x3f6b041b, 0x3f6c1dc9, 0x3f6d3795, 0x3f6e5180, 0x3f6f6b8b, + 0x3f7085b2, 0x3f719ff7, 0x3f72ba5b, 0x3f73d4dc, 0x3f74ef7c, 0x3f760a38, 0x3f772512, 0x3f78400b, + 0x3f795b20, 0x3f7a7651, 0x3f7b91a2, 0x3f7cad0e, 0x3f7dc896, 0x3f7ee43c, 0x3f800000, 0x3f800000 +}; + + +#pragma prefast(suppress : 25000, "FXMVECTOR is 16 bytes") +static inline XMVECTOR _TableDecodeGamma22( FXMVECTOR v ) +{ + float f[4]; + XMStoreFloat4( (XMFLOAT4*)f, v ); + + for( size_t i=0; i < 4; ++i ) + { + float f2 = f[i] * f[i] * 254.0f; + uint32_t i2 = static_cast(f2); + i2 = std::min( i2, _countof(g_fDecodeGamma22)-2 ); + + float fS = f2 - (float) i2; + float fA = ((float *) g_fDecodeGamma22)[i2]; + float fB = ((float *) g_fDecodeGamma22)[i2 + 1]; + + f[i] = fA + fS * (fB - fA); + } + + return XMLoadFloat4( (XMFLOAT4*)f ); +} + + +//------------------------------------------------------------------------------------- +// Convert scanline based on source/target formats +//------------------------------------------------------------------------------------- +struct ConvertData +{ + DXGI_FORMAT format; + size_t datasize; + DWORD flags; +}; + +static const ConvertData g_ConvertTable[] = { + { DXGI_FORMAT_R32G32B32A32_FLOAT, 32, CONVF_FLOAT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R32G32B32A32_UINT, 32, CONVF_UINT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R32G32B32A32_SINT, 32, CONVF_SINT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R32G32B32_FLOAT, 32, CONVF_FLOAT | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_R32G32B32_UINT, 32, CONVF_UINT | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_R32G32B32_SINT, 32, CONVF_SINT | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_R16G16B16A16_FLOAT, 16, CONVF_FLOAT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R16G16B16A16_UNORM, 16, CONVF_UNORM | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R16G16B16A16_UINT, 16, CONVF_UINT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R16G16B16A16_SNORM, 16, CONVF_SNORM | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R16G16B16A16_SINT, 16, CONVF_SINT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R32G32_FLOAT, 32, CONVF_FLOAT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R32G32_UINT, 32, CONVF_UINT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R32G32_SINT, 32, CONVF_SINT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 32, CONVF_FLOAT | CONVF_DEPTH | CONVF_STENCIL }, + { DXGI_FORMAT_R10G10B10A2_UNORM, 10, CONVF_UNORM | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R10G10B10A2_UINT, 10, CONVF_UINT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R11G11B10_FLOAT, 10, CONVF_FLOAT | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_R8G8B8A8_UNORM, 8, CONVF_UNORM | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, 8, CONVF_UNORM | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R8G8B8A8_UINT, 8, CONVF_UINT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R8G8B8A8_SNORM, 8, CONVF_SNORM | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R8G8B8A8_SINT, 8, CONVF_SINT | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_R16G16_FLOAT, 16, CONVF_FLOAT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R16G16_UNORM, 16, CONVF_UNORM | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R16G16_UINT, 16, CONVF_UINT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R16G16_SNORM, 16, CONVF_SNORM | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R16G16_SINT, 16, CONVF_SINT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_D32_FLOAT, 32, CONVF_FLOAT | CONVF_DEPTH }, + { DXGI_FORMAT_R32_FLOAT, 32, CONVF_FLOAT | CONVF_R }, + { DXGI_FORMAT_R32_UINT, 32, CONVF_UINT | CONVF_R }, + { DXGI_FORMAT_R32_SINT, 32, CONVF_SINT | CONVF_R }, + { DXGI_FORMAT_D24_UNORM_S8_UINT, 32, CONVF_UNORM | CONVF_DEPTH | CONVF_STENCIL }, + { DXGI_FORMAT_R8G8_UNORM, 8, CONVF_UNORM | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R8G8_UINT, 8, CONVF_UINT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R8G8_SNORM, 8, CONVF_SNORM | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R8G8_SINT, 8, CONVF_SINT | CONVF_R | CONVF_G }, + { DXGI_FORMAT_R16_FLOAT, 16, CONVF_FLOAT | CONVF_R }, + { DXGI_FORMAT_D16_UNORM, 16, CONVF_UNORM | CONVF_DEPTH }, + { DXGI_FORMAT_R16_UNORM, 16, CONVF_UNORM | CONVF_R }, + { DXGI_FORMAT_R16_UINT, 16, CONVF_UINT | CONVF_R }, + { DXGI_FORMAT_R16_SNORM, 16, CONVF_SNORM | CONVF_R }, + { DXGI_FORMAT_R16_SINT, 16, CONVF_SINT | CONVF_R }, + { DXGI_FORMAT_R8_UNORM, 8, CONVF_UNORM | CONVF_R }, + { DXGI_FORMAT_R8_UINT, 8, CONVF_UINT | CONVF_R }, + { DXGI_FORMAT_R8_SNORM, 8, CONVF_SNORM | CONVF_R }, + { DXGI_FORMAT_R8_SINT, 8, CONVF_SINT | CONVF_R }, + { DXGI_FORMAT_A8_UNORM, 8, CONVF_UNORM | CONVF_A }, + { DXGI_FORMAT_R1_UNORM, 1, CONVF_UNORM | CONVF_R }, + { DXGI_FORMAT_R9G9B9E5_SHAREDEXP, 9, CONVF_SHAREDEXP | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_R8G8_B8G8_UNORM, 8, CONVF_UNORM | CONVF_PACKED | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_G8R8_G8B8_UNORM, 8, CONVF_UNORM | CONVF_PACKED | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_BC1_UNORM, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC1_UNORM_SRGB, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC2_UNORM, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC2_UNORM_SRGB, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC3_UNORM, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC3_UNORM_SRGB, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC4_UNORM, 8, CONVF_UNORM | CONVF_BC | CONVF_R }, + { DXGI_FORMAT_BC4_SNORM, 8, CONVF_SNORM | CONVF_BC | CONVF_R }, + { DXGI_FORMAT_BC5_UNORM, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G }, + { DXGI_FORMAT_BC5_SNORM, 8, CONVF_SNORM | CONVF_BC | CONVF_R | CONVF_G }, + { DXGI_FORMAT_B5G6R5_UNORM, 5, CONVF_UNORM | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_B5G5R5A1_UNORM, 5, CONVF_UNORM | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_B8G8R8A8_UNORM, 8, CONVF_UNORM | CONVF_BGR | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_B8G8R8X8_UNORM, 8, CONVF_UNORM | CONVF_BGR | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM, 10, CONVF_UNORM | CONVF_X2 | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, 8, CONVF_UNORM | CONVF_BGR | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, 8, CONVF_UNORM | CONVF_BGR | CONVF_R | CONVF_G | CONVF_B }, + { DXGI_FORMAT_BC6H_UF16, 16, CONVF_FLOAT | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC6H_SF16, 16, CONVF_FLOAT | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC7_UNORM, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, + { DXGI_FORMAT_BC7_UNORM_SRGB, 8, CONVF_UNORM | CONVF_BC | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, +#ifdef DXGI_1_2_FORMATS + { DXGI_FORMAT_B4G4R4A4_UNORM, 4, CONVF_UNORM | CONVF_BGR | CONVF_R | CONVF_G | CONVF_B | CONVF_A }, +#endif +}; + +#pragma prefast( suppress : 25004, "Signature must match bsearch_s" ); +static int __cdecl _ConvertCompare( void *context, const void* ptr1, const void *ptr2 ) +{ + UNREFERENCED_PARAMETER(context); + const ConvertData *p1 = reinterpret_cast(ptr1); + const ConvertData *p2 = reinterpret_cast(ptr2); + if ( p1->format == p2->format ) return 0; + else return (p1->format < p2->format ) ? -1 : 1; +} + +DWORD _GetConvertFlags( DXGI_FORMAT format ) +{ +#ifdef DEBUG + // Ensure conversion table is in ascending order + assert( _countof(g_ConvertTable) > 0 ); + DXGI_FORMAT lastvalue = g_ConvertTable[0].format; + for( size_t index=1; index < _countof(g_ConvertTable); ++index ) + { + assert( g_ConvertTable[index].format > lastvalue ); + lastvalue = g_ConvertTable[index].format; + } +#endif + + ConvertData key = { format, 0 }; + const ConvertData* in = (const ConvertData*) bsearch_s( &key, g_ConvertTable, _countof(g_ConvertTable), sizeof(ConvertData), + _ConvertCompare, 0 ); + return (in) ? in->flags : 0; +} + +void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, DXGI_FORMAT inFormat, DWORD flags ) +{ + assert( pBuffer && count > 0 && (((uintptr_t)pBuffer & 0xF) == 0) ); + assert( IsValid(outFormat) && !IsVideo(outFormat) && !IsTypeless(outFormat) ); + assert( IsValid(inFormat) && !IsVideo(inFormat) && !IsTypeless(inFormat) ); + + if ( !pBuffer ) + return; + +#ifdef DEBUG + // Ensure conversion table is in ascending order + assert( _countof(g_ConvertTable) > 0 ); + DXGI_FORMAT lastvalue = g_ConvertTable[0].format; + for( size_t index=1; index < _countof(g_ConvertTable); ++index ) + { + assert( g_ConvertTable[index].format > lastvalue ); + lastvalue = g_ConvertTable[index].format; + } +#endif + + // Determine conversion details about source and dest formats + ConvertData key = { inFormat, 0 }; + const ConvertData* in = (const ConvertData*) bsearch_s( &key, g_ConvertTable, _countof(g_ConvertTable), sizeof(ConvertData), + _ConvertCompare, 0 ); + key.format = outFormat; + const ConvertData* out = (const ConvertData*) bsearch_s( &key, g_ConvertTable, _countof(g_ConvertTable), sizeof(ConvertData), + _ConvertCompare, 0 ); + if ( !in || !out ) + { + assert(false); + return; + } + + assert( _GetConvertFlags( inFormat ) == in->flags ); + assert( _GetConvertFlags( outFormat ) == out->flags ); + + // Handle SRGB filtering modes + if ( IsSRGB( inFormat ) ) + flags |= TEX_FILTER_SRGB_IN; + + if ( IsSRGB( outFormat ) ) + flags |= TEX_FILTER_SRGB_OUT; + + if ( in->flags & CONVF_SNORM ) + flags &= ~TEX_FILTER_SRGB_IN; + + if ( out->flags & CONVF_SNORM ) + flags &= ~TEX_FILTER_SRGB_OUT; + + if ( (flags & (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT)) == (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT) ) + { + flags &= ~(TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT); + } + + // sRGB input processing (sRGB -> RGB) + if ( flags & TEX_FILTER_SRGB_IN ) + { + if ( (in->flags & CONVF_FLOAT) || (in->flags & CONVF_UNORM) ) + { + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + // rgb = rgb^(2.2); a=a + XMVECTOR v = *ptr; + XMVECTOR v1 = _TableDecodeGamma22( v ); + *ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 ); + } + } + } + + // Handle conversion special cases + DWORD diffFlags = in->flags ^ out->flags; + if ( diffFlags != 0) + { + if ( out->flags & CONVF_UNORM ) + { + if ( in->flags & CONVF_SNORM ) + { + // SNORM -> UNORM + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + XMVECTOR v = *ptr; + *ptr++ = XMVectorMultiplyAdd( v, g_XMOneHalf, g_XMOneHalf ); + } + } + else if ( in->flags & CONVF_FLOAT ) + { + // FLOAT -> UNORM + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + XMVECTOR v = *ptr; + *ptr++ = XMVectorSaturate( v ); + } + } + } + else if ( out->flags & CONVF_SNORM ) + { + if ( in->flags & CONVF_UNORM ) + { + // UNORM -> SNORM + static XMVECTORF32 two = { 2.0f, 2.0f, 2.0f, 2.0f }; + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + XMVECTOR v = *ptr; + *ptr++ = XMVectorMultiplyAdd( v, two, g_XMNegativeOne ); + } + } + else if ( in->flags & CONVF_FLOAT ) + { + // FLOAT -> SNORM + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + XMVECTOR v = *ptr; + *ptr++ = XMVectorClamp( v, g_XMNegativeOne, g_XMOne ); + } + } + } + + // !CONVF_A -> CONVF_A is handled because LoadScanline ensures alpha defaults to 1.0 for no-alpha formats + + // CONVF_PACKED cases are handled because LoadScanline/StoreScanline handles packing/unpacking + + if ( ((out->flags & CONVF_RGBA_MASK) == CONVF_A) && !(in->flags & CONVF_A) ) + { + // !CONVF_A -> A format + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + XMVECTOR v = *ptr; + *ptr++ = XMVectorSplatX( v ); + } + } + else if ( ((in->flags & CONVF_RGBA_MASK) == CONVF_A) && !(out->flags & CONVF_A) ) + { + // A format -> !CONVF_A + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + XMVECTOR v = *ptr; + *ptr++ = XMVectorSplatW( v ); + } + } + else if ( ((in->flags & CONVF_RGB_MASK) == CONVF_R) && ((out->flags & CONVF_RGB_MASK) == (CONVF_R|CONVF_G|CONVF_B)) ) + { + // R format -> RGB format + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + XMVECTOR v = *ptr; + XMVECTOR v1 = XMVectorSplatX( v ); + *ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 ); + } + } + } + + // sRGB output processing (RGB -> sRGB) + if ( flags & TEX_FILTER_SRGB_OUT ) + { + if ( (out->flags & CONVF_FLOAT) || (out->flags & CONVF_UNORM) ) + { + XMVECTOR* ptr = pBuffer; + for( size_t i=0; i < count; ++i ) + { + // rgb = rgb^(1/2.2); a=a + XMVECTOR v = *ptr; + XMVECTOR v1 = _TableEncodeGamma22( v ); + *ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 ); + } + } + } +} + + +//------------------------------------------------------------------------------------- +// Convert the source image using WIC +//------------------------------------------------------------------------------------- +static HRESULT _ConvertUsingWIC( _In_ const Image& srcImage, _In_ const WICPixelFormatGUID& pfGUID, + _In_ const WICPixelFormatGUID& targetGUID, + _In_ DWORD filter, _In_ float threshold, _In_ const Image& destImage ) +{ + assert( srcImage.width == destImage.width ); + assert( srcImage.height == destImage.height ); + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject FC; + HRESULT hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + // Need to implement usage of TEX_FILTER_SRGB_IN/TEX_FILTER_SRGB_OUT + + BOOL canConvert = FALSE; + hr = FC->CanConvert( pfGUID, targetGUID, &canConvert ); + if ( FAILED(hr) || !canConvert ) + { + // This case is not an issue for the subset of WIC formats that map directly to DXGI + return E_UNEXPECTED; + } + + ScopedObject source; + hr = pWIC->CreateBitmapFromMemory( static_cast( srcImage.width ), static_cast( srcImage.height ), pfGUID, + static_cast( srcImage.rowPitch ), static_cast( srcImage.slicePitch ), + srcImage.pixels, &source ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( source.Get(), targetGUID, _GetWICDither( filter ), 0, threshold, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( destImage.rowPitch ), static_cast( destImage.slicePitch ), destImage.pixels ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Convert the source using WIC and then convert to DXGI format from there +//------------------------------------------------------------------------------------- +static HRESULT _ConvertFromWIC( _In_ const Image& srcImage, _In_ const WICPixelFormatGUID& pfGUID, + _In_ DWORD filter, _In_ float threshold, _In_ const Image& destImage ) +{ + assert( srcImage.width == destImage.width ); + assert( srcImage.height == destImage.height ); + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject FC; + HRESULT hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + BOOL canConvert = FALSE; + hr = FC->CanConvert( pfGUID, GUID_WICPixelFormat128bppRGBAFloat, &canConvert ); + if ( FAILED(hr) || !canConvert ) + { + // This case is not an issue for the subset of WIC formats that map directly to DXGI + return E_UNEXPECTED; + } + + ScratchImage temp; + hr = temp.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.width, srcImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *timg = temp.GetImage( 0, 0, 0 ); + if ( !timg ) + return E_POINTER; + + ScopedObject source; + hr = pWIC->CreateBitmapFromMemory( static_cast( srcImage.width ), static_cast( srcImage.height ), pfGUID, + static_cast( srcImage.rowPitch ), static_cast( srcImage.slicePitch ), + srcImage.pixels, &source ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( source.Get(), GUID_WICPixelFormat128bppRGBAFloat, _GetWICDither( filter ), 0, threshold, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( timg->rowPitch ), static_cast( timg->slicePitch ), timg->pixels ); + if ( FAILED(hr) ) + return hr; + + // Perform conversion on temp image which is now in R32G32B32A32_FLOAT format to final image + uint8_t *pSrc = timg->pixels; + uint8_t *pDest = destImage.pixels; + if ( !pSrc || !pDest ) + return E_POINTER; + + for( size_t h = 0; h < srcImage.height; ++h ) + { + _ConvertScanline( reinterpret_cast(pSrc), srcImage.width, destImage.format, DXGI_FORMAT_R32G32B32A32_FLOAT, filter ); + + if ( !_StoreScanline( pDest, destImage.rowPitch, destImage.format, reinterpret_cast(pSrc), srcImage.width ) ) + return E_FAIL; + + pSrc += timg->rowPitch; + pDest += destImage.rowPitch; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Convert the source from DXGI format then use WIC to convert to final format +//------------------------------------------------------------------------------------- +static HRESULT _ConvertToWIC( _In_ const Image& srcImage, + _In_ const WICPixelFormatGUID& targetGUID, _In_ DWORD filter, _In_ float threshold, _In_ const Image& destImage ) +{ + assert( srcImage.width == destImage.width ); + assert( srcImage.height == destImage.height ); + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject FC; + HRESULT hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + BOOL canConvert = FALSE; + hr = FC->CanConvert( GUID_WICPixelFormat128bppRGBAFloat, targetGUID, &canConvert ); + if ( FAILED(hr) || !canConvert ) + { + // This case is not an issue for the subset of WIC formats that map directly to DXGI + return E_UNEXPECTED; + } + + ScratchImage temp; + hr = temp.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.width, srcImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *timg = temp.GetImage( 0, 0, 0 ); + if ( !timg ) + return E_POINTER; + + const uint8_t *pSrc = srcImage.pixels; + if ( !pSrc ) + return E_POINTER; + + uint8_t *pDest = timg->pixels; + if ( !pDest ) + return E_POINTER; + + for( size_t h = 0; h < srcImage.height; ++h ) + { + if ( !_LoadScanline( reinterpret_cast(pDest), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format ) ) + return E_FAIL; + + _ConvertScanline( reinterpret_cast(pDest), srcImage.width, DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.format, filter ); + + pSrc += srcImage.rowPitch; + pDest += timg->rowPitch; + } + + // Perform conversion on temp image which is now in R32G32B32A32_FLOAT format + ScopedObject source; + hr = pWIC->CreateBitmapFromMemory( static_cast( timg->width ), static_cast( timg->height ), GUID_WICPixelFormat128bppRGBAFloat, + static_cast( timg->rowPitch ), static_cast( timg->slicePitch ), + timg->pixels, &source ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( source.Get(), targetGUID, _GetWICDither( filter ), 0, threshold, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( destImage.rowPitch ), static_cast( destImage.slicePitch ), destImage.pixels ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Convert the source image (not using WIC) +//------------------------------------------------------------------------------------- +static HRESULT _Convert( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage ) +{ + assert( srcImage.width == destImage.width ); + assert( srcImage.height == destImage.height ); + + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*srcImage.width), 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + const uint8_t *pSrc = srcImage.pixels; + uint8_t *pDest = destImage.pixels; + if ( !pSrc || !pDest ) + return E_POINTER; + + for( size_t h = 0; h < srcImage.height; ++h ) + { + if ( !_LoadScanline( scanline.get(), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format ) ) + return E_FAIL; + + _ConvertScanline( scanline.get(), srcImage.width, destImage.format, srcImage.format, filter ); + + if ( !_StoreScanline( pDest, destImage.rowPitch, destImage.format, scanline.get(), srcImage.width ) ) + return E_FAIL; + + pSrc += srcImage.rowPitch; + pDest += destImage.rowPitch; + } + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Convert image +//------------------------------------------------------------------------------------- +HRESULT Convert( const Image& srcImage, DXGI_FORMAT format, DWORD filter, float threshold, ScratchImage& image ) +{ + if ( (srcImage.format == format) || !IsValid( format ) ) + return E_INVALIDARG; + + if ( !srcImage.pixels ) + return E_POINTER; + + if ( IsCompressed(srcImage.format) || IsCompressed(format) + || IsVideo(srcImage.format) || IsVideo(format) + || IsTypeless(srcImage.format) || IsTypeless(format) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + +#ifdef _AMD64_ + if ( (srcImage.width > 0xFFFFFFFF) || (srcImage.height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + HRESULT hr = image.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *rimage = image.GetImage( 0, 0, 0 ); + if ( !rimage ) + { + image.Release(); + return E_POINTER; + } + + WICPixelFormatGUID pfGUID; + if ( _DXGIToWIC( srcImage.format, pfGUID ) ) + { + WICPixelFormatGUID targetGUID; + if ( _DXGIToWIC( format, targetGUID ) ) + { + // Case 1: Both source and target formats are WIC supported + hr = _ConvertUsingWIC( srcImage, pfGUID, targetGUID, filter, threshold, *rimage ); + } + else + { + // Case 2: Source format is supported by WIC, but not the target format + hr = _ConvertFromWIC( srcImage, pfGUID, filter, threshold, *rimage ); + } + } + else + { + WICPixelFormatGUID targetGUID; + if ( _DXGIToWIC( format, targetGUID ) ) + { + // Case 3: Source format is not supported by WIC, but does support the target format + hr = _ConvertToWIC( srcImage, targetGUID, filter, threshold, *rimage ); + } + else + { + // Case 4: Both source and target format are not supported by WIC + hr = _Convert( srcImage, filter, *rimage ); + } + } + + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Convert image (complex) +//------------------------------------------------------------------------------------- +HRESULT Convert( const Image* srcImages, size_t nimages, const TexMetadata& metadata, + DXGI_FORMAT format, DWORD filter, float threshold, ScratchImage& result ) +{ + if ( !srcImages || !nimages || (metadata.format == format) || !IsValid(format) ) + return E_INVALIDARG; + + if ( IsCompressed(metadata.format) || IsCompressed(format) + || IsVideo(metadata.format) || IsVideo(format) + || IsTypeless(metadata.format) || IsTypeless(format) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + +#ifdef _AMD64_ + if ( (metadata.width > 0xFFFFFFFF) || (metadata.height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + TexMetadata mdata2 = metadata; + mdata2.format = format; + HRESULT hr = result.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + if ( nimages != result.GetImageCount() ) + { + result.Release(); + return E_FAIL; + } + + const Image* dest = result.GetImages(); + if ( !dest ) + { + result.Release(); + return E_POINTER; + } + + WICPixelFormatGUID pfGUID, targetGUID; + bool wicpf = _DXGIToWIC( metadata.format, pfGUID ); + bool wictargetpf = _DXGIToWIC( format, targetGUID ); + + for( size_t index=0; index < nimages; ++index ) + { + const Image& src = srcImages[ index ]; + if ( src.format != metadata.format ) + { + result.Release(); + return E_FAIL; + } + +#ifdef _AMD64_ + if ( (src.width > 0xFFFFFFFF) || (src.height > 0xFFFFFFFF) ) + return E_FAIL; +#endif + + const Image& dst = dest[ index ]; + assert( dst.format == format ); + + if ( src.width != dst.width || src.height != dst.height ) + { + result.Release(); + return E_FAIL; + } + + if ( wicpf ) + { + if ( wictargetpf ) + { + // Case 1: Both source and target formats are WIC supported + hr = _ConvertUsingWIC( src, pfGUID, targetGUID, filter, threshold, dst ); + } + else + { + // Case 2: Source format is supported by WIC, but not the target format + hr = _ConvertFromWIC( src, pfGUID, filter, threshold, dst ); + } + } + else + { + if ( wictargetpf ) + { + // Case 3: Source format is not supported by WIC, but does support the target format + hr = _ConvertToWIC( src, targetGUID, filter, threshold, dst ); + } + else + { + // Case 4: Both source and target format are not supported by WIC + hr = _Convert( src, filter, dst ); + } + } + + if ( FAILED(hr) ) + { + result.Release(); + return hr; + } + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexD3D11.cpp b/DirectXTex/DirectXTexD3D11.cpp new file mode 100644 index 0000000..a5a8327 --- /dev/null +++ b/DirectXTex/DirectXTexD3D11.cpp @@ -0,0 +1,800 @@ +//------------------------------------------------------------------------------------- +// DirectXTexD3D11.cpp +// +// DirectX Texture Library - Direct3D 11 helpers +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +#include + +namespace DirectX +{ + +static HRESULT _Capture( _In_ ID3D11DeviceContext* pContext, _In_ ID3D11Resource* pSource, _In_ const TexMetadata& metadata, + _In_ const ScratchImage& result ) +{ + if ( !pContext || !pSource || !result.GetPixels() ) + return E_POINTER; + + if ( metadata.dimension == TEX_DIMENSION_TEXTURE3D ) + { + //--- Volume texture ---------------------------------------------------------- + assert( metadata.arraySize == 1 ); + + size_t height = metadata.height; + size_t depth = metadata.depth; + + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + UINT dindex = D3D11CalcSubresource( static_cast( level ), 0, static_cast( metadata.mipLevels ) ); + + D3D11_MAPPED_SUBRESOURCE mapped; + HRESULT hr = pContext->Map( pSource, dindex, D3D11_MAP_READ, 0, &mapped ); + if ( FAILED(hr) ) + return hr; + + const uint8_t* pslice = reinterpret_cast( mapped.pData ); + if ( !pslice ) + { + pContext->Unmap( pSource, dindex ); + return E_POINTER; + } + + size_t lines = ComputeScanlines( metadata.format, height ); + + for( size_t slice = 0; slice < depth; ++slice ) + { + const Image* img = result.GetImage( level, 0, slice ); + if ( !img ) + { + pContext->Unmap( pSource, dindex ); + return E_FAIL; + } + + if ( !img->pixels ) + { + pContext->Unmap( pSource, dindex ); + return E_POINTER; + } + + const uint8_t* sptr = pslice; + uint8_t* dptr = img->pixels; + for( size_t h = 0; h < lines; ++h ) + { + size_t msize = std::min( img->rowPitch, mapped.RowPitch ); + memcpy_s( dptr, img->rowPitch, sptr, msize ); + sptr += mapped.RowPitch; + dptr += img->rowPitch; + } + + pslice += mapped.DepthPitch; + } + + pContext->Unmap( pSource, dindex ); + + if ( height > 1 ) + height >>= 1; + if ( depth > 1 ) + depth >>= 1; + } + } + else + { + //--- 1D or 2D texture -------------------------------------------------------- + assert( metadata.depth == 1 ); + + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + size_t height = metadata.height; + + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + UINT dindex = D3D11CalcSubresource( static_cast( level ), static_cast( item ), static_cast( metadata.mipLevels ) ); + + D3D11_MAPPED_SUBRESOURCE mapped; + HRESULT hr = pContext->Map( pSource, dindex, D3D11_MAP_READ, 0, &mapped ); + if ( FAILED(hr) ) + return hr; + + const Image* img = result.GetImage( level, item, 0 ); + if ( !img ) + { + pContext->Unmap( pSource, dindex ); + return E_FAIL; + } + + if ( !img->pixels ) + { + pContext->Unmap( pSource, dindex ); + return E_POINTER; + } + + size_t lines = ComputeScanlines( metadata.format, height ); + + const uint8_t* sptr = reinterpret_cast( mapped.pData ); + uint8_t* dptr = img->pixels; + for( size_t h = 0; h < lines; ++h ) + { + size_t msize = std::min( img->rowPitch, mapped.RowPitch ); + memcpy_s( dptr, img->rowPitch, sptr, msize ); + sptr += mapped.RowPitch; + dptr += img->rowPitch; + } + + pContext->Unmap( pSource, dindex ); + + if ( height > 1 ) + height >>= 1; + } + } + } + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Determine if given texture metadata is supported on the given device +//------------------------------------------------------------------------------------- +bool IsSupportedTexture( ID3D11Device* pDevice, const TexMetadata& metadata ) +{ + if ( !pDevice ) + return false; + + D3D_FEATURE_LEVEL fl = pDevice->GetFeatureLevel(); + + // Validate format + DXGI_FORMAT fmt = metadata.format; + + if ( !IsValid( fmt ) ) + return false; + + if ( IsVideo(fmt) ) + return false; + + switch( fmt ) + { + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + if ( fl < D3D_FEATURE_LEVEL_10_0 ) + return false; + break; + + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + if ( fl < D3D_FEATURE_LEVEL_11_0 ) + return false; + break; + } + + // Validate miplevel count + if ( metadata.mipLevels > D3D11_REQ_MIP_LEVELS ) + return false; + + // Validate array size, dimension, and width/height + size_t arraySize = metadata.arraySize; + size_t iWidth = metadata.width; + size_t iHeight = metadata.height; + size_t iDepth = metadata.depth; + + // Most cases are known apriori based on feature level, but we use this for robustness to handle the few optional cases + UINT formatSupport = 0; + pDevice->CheckFormatSupport( fmt, &formatSupport ); + + switch ( metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + if ( !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE1D) ) + return false; + + if ( (arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) + || (iWidth > D3D11_REQ_TEXTURE1D_U_DIMENSION) ) + return false; + + if ( fl < D3D_FEATURE_LEVEL_11_0 ) + { + if ( (arraySize > D3D10_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) + || (iWidth > D3D10_REQ_TEXTURE1D_U_DIMENSION) ) + return false; + + if ( fl < D3D_FEATURE_LEVEL_10_0 ) + { + if ( (arraySize > 1) || (iWidth > 4096 /*D3D_FL9_3_REQ_TEXTURE1D_U_DIMENSION*/) ) + return false; + + if ( (fl < D3D_FEATURE_LEVEL_9_3) && (iWidth > 2048 /*D3D_FL9_1_REQ_TEXTURE1D_U_DIMENSION*/ ) ) + return false; + } + } + break; + + case TEX_DIMENSION_TEXTURE2D: + if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) + { + if ( !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE) ) + return false; + + if ( (arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) + || (iWidth > D3D11_REQ_TEXTURECUBE_DIMENSION) + || (iHeight > D3D11_REQ_TEXTURECUBE_DIMENSION)) + return false; + + if ( fl < D3D_FEATURE_LEVEL_11_0 ) + { + if ( (arraySize > D3D10_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) + || (iWidth > D3D10_REQ_TEXTURECUBE_DIMENSION) + || (iHeight > D3D10_REQ_TEXTURECUBE_DIMENSION)) + return false; + + if ( (fl < D3D_FEATURE_LEVEL_10_1) && (arraySize != 6) ) + return false; + + if ( fl < D3D_FEATURE_LEVEL_10_0 ) + { + if ( (iWidth > 4096 /*D3D_FL9_3_REQ_TEXTURECUBE_DIMENSION*/ ) + || (iHeight > 4096 /*D3D_FL9_3_REQ_TEXTURECUBE_DIMENSION*/ ) ) + return false; + + if ( (fl < D3D_FEATURE_LEVEL_9_3) + && ( (iWidth > 512 /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/) + || (iHeight > 512 /*D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION*/) ) ) + return false; + } + } + } + else // Not a cube map + { + if ( !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D) ) + return false; + + if ( (arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) + || (iWidth > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) + || (iHeight > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + return false; + + if ( fl < D3D_FEATURE_LEVEL_11_0 ) + { + if ( (arraySize > D3D10_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) + || (iWidth > D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION) + || (iHeight > D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + return false; + + if ( fl < D3D_FEATURE_LEVEL_10_0 ) + { + if ( (arraySize > 1) + || (iWidth > 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/) + || (iHeight > 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/) ) + return false; + + if ( (fl < D3D_FEATURE_LEVEL_9_3) + && ( (iWidth > 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/) + || (iHeight > 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/) ) ) + return false; + } + } + } + break; + + case TEX_DIMENSION_TEXTURE3D: + if ( !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE3D) ) + return false; + + if ( (arraySize > 1) + || (iWidth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) + || (iHeight > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) + || (iDepth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ) + return false; + + if ( fl < D3D_FEATURE_LEVEL_11_0 ) + { + if ( (iWidth > D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) + || (iHeight > D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) + || (iDepth > D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) ) + return false; + + if ( fl < D3D_FEATURE_LEVEL_10_0 ) + { + if ( (iWidth > 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/) + || (iHeight > 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/) + || (iDepth > 256 /*D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/) ) + return false; + } + } + break; + + default: + // Not a supported dimension + return false; + } + + return true; +} + + +//------------------------------------------------------------------------------------- +// Create a texture resource +//------------------------------------------------------------------------------------- +HRESULT CreateTexture( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata, + ID3D11Resource** ppResource ) +{ + if ( !pDevice || !srcImages || !nimages || !ppResource ) + return E_INVALIDARG; + + if ( !metadata.mipLevels || !metadata.arraySize ) + return E_INVALIDARG; + +#ifdef _AMD64_ + if ( (metadata.width > 0xFFFFFFFF) || (metadata.height > 0xFFFFFFFF) + || (metadata.mipLevels > 0xFFFFFFFF) || (metadata.arraySize > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + std::unique_ptr initData( new D3D11_SUBRESOURCE_DATA[ metadata.mipLevels * metadata.arraySize ] ); + if ( !initData ) + return E_OUTOFMEMORY; + + // Fill out subresource array + if ( metadata.dimension == TEX_DIMENSION_TEXTURE3D ) + { + //--- Volume case ------------------------------------------------------------- + if ( !metadata.depth ) + return E_INVALIDARG; + +#ifdef _AMD64_ + if ( metadata.depth > 0xFFFFFFFF ) + return E_INVALIDARG; +#endif + + if ( metadata.arraySize > 1 ) + // Direct3D 11 doesn't support arrays of 3D textures + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + size_t depth = metadata.depth; + + size_t idx = 0; + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + size_t index = metadata.ComputeIndex( level, 0, 0 ); + if ( index >= nimages ) + return E_FAIL; + + const Image& img = srcImages[ index ]; + + if ( img.format != metadata.format ) + return E_FAIL; + + if ( !img.pixels ) + return E_POINTER; + + // Verify pixels in image 1 .. (depth-1) are exactly image->slicePitch apart + // For 3D textures, this relies on all slices of the same miplevel being continous in memory + // (this is how ScratchImage lays them out), which is why we just give the 0th slice to Direct3D 11 + const uint8_t* pSlice = img.pixels + img.slicePitch; + for( size_t slice = 1; slice < depth; ++slice ) + { + size_t tindex = metadata.ComputeIndex( level, 0, slice ); + if ( tindex >= nimages ) + return E_FAIL; + + const Image& timg = srcImages[ tindex ]; + + if ( !timg.pixels ) + return E_POINTER; + + if ( timg.pixels != pSlice + || timg.format != metadata.format + || timg.rowPitch != img.rowPitch + || timg.slicePitch != img.slicePitch ) + return E_FAIL; + + pSlice = timg.pixels + img.slicePitch; + } + + assert( idx < (metadata.mipLevels * metadata.arraySize) ); + + initData[idx].pSysMem = img.pixels; + initData[idx].SysMemPitch = static_cast( img.rowPitch ); + initData[idx].SysMemSlicePitch = static_cast( img.slicePitch ); + ++idx; + + if ( depth > 1 ) + depth >>= 1; + } + } + else + { + //--- 1D or 2D texture case --------------------------------------------------- + size_t idx = 0; + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + size_t index = metadata.ComputeIndex( level, item, 0 ); + if ( index >= nimages ) + return E_FAIL; + + const Image& img = srcImages[ index ]; + + if ( img.format != metadata.format ) + return E_FAIL; + + if ( !img.pixels ) + return E_POINTER; + + assert( idx < (metadata.mipLevels * metadata.arraySize) ); + + initData[idx].pSysMem = img.pixels; + initData[idx].SysMemPitch = static_cast( img.rowPitch ); + initData[idx].SysMemSlicePitch = static_cast( img.slicePitch ); + ++idx; + } + } + } + + // Create texture using static initialization data + HRESULT hr = E_FAIL; + + switch ( metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + { + D3D11_TEXTURE1D_DESC desc; + desc.Width = static_cast( metadata.width ); + desc.MipLevels = static_cast( metadata.mipLevels ); + desc.ArraySize = static_cast( metadata.arraySize ); + desc.Format = metadata.format; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + hr = pDevice->CreateTexture1D( &desc, initData.get(), reinterpret_cast(ppResource) ); + } + break; + + case TEX_DIMENSION_TEXTURE2D: + { + D3D11_TEXTURE2D_DESC desc; + desc.Width = static_cast( metadata.width ); + desc.Height = static_cast( metadata.height ); + desc.MipLevels = static_cast( metadata.mipLevels ); + desc.ArraySize = static_cast( metadata.arraySize ); + desc.Format = metadata.format; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = (metadata.miscFlags & TEX_MISC_TEXTURECUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + + hr = pDevice->CreateTexture2D( &desc, initData.get(), reinterpret_cast(ppResource) ); + } + break; + + case TEX_DIMENSION_TEXTURE3D: + { + D3D11_TEXTURE3D_DESC desc; + desc.Width = static_cast( metadata.width ); + desc.Height = static_cast( metadata.height ); + desc.Depth = static_cast( metadata.depth ); + desc.MipLevels = static_cast( metadata.mipLevels ); + desc.Format = metadata.format; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + hr = pDevice->CreateTexture3D( &desc, initData.get(), reinterpret_cast(ppResource) ); + } + break; + } + + return hr; +} + + +//------------------------------------------------------------------------------------- +// Create a shader resource view and associated texture +//------------------------------------------------------------------------------------- +HRESULT CreateShaderResourceView( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata, + ID3D11ShaderResourceView** ppSRV ) +{ + if ( !ppSRV ) + return E_INVALIDARG; + + ScopedObject resource; + HRESULT hr = CreateTexture( pDevice, srcImages, nimages, metadata, &resource ); + if ( FAILED(hr) ) + return hr; + + assert( !resource.IsNull() ); + + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + memset( &SRVDesc, 0, sizeof(SRVDesc) ); + SRVDesc.Format = metadata.format; + + switch ( metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + if ( metadata.arraySize > 1 ) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY; + SRVDesc.Texture1DArray.MipLevels = static_cast( metadata.mipLevels ); + SRVDesc.Texture1DArray.ArraySize = static_cast( metadata.arraySize ); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D; + SRVDesc.Texture1D.MipLevels = static_cast( metadata.mipLevels ); + } + break; + + case TEX_DIMENSION_TEXTURE2D: + if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) + { + if (metadata.arraySize > 6) + { + assert( (metadata.arraySize % 6) == 0 ); + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBEARRAY; + SRVDesc.TextureCubeArray.MipLevels = static_cast( metadata.mipLevels ); + SRVDesc.TextureCubeArray.NumCubes = static_cast( metadata.arraySize / 6 ); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + SRVDesc.TextureCube.MipLevels = static_cast( metadata.mipLevels ); + } + } + else if ( metadata.arraySize > 1 ) + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + SRVDesc.Texture2DArray.MipLevels = static_cast( metadata.mipLevels ); + SRVDesc.Texture2DArray.ArraySize = static_cast( metadata.arraySize ); + } + else + { + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + SRVDesc.Texture2D.MipLevels = static_cast( metadata.mipLevels ); + } + break; + + case TEX_DIMENSION_TEXTURE3D: + assert( metadata.arraySize == 1 ); + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + SRVDesc.Texture3D.MipLevels = static_cast( metadata.mipLevels ); + break; + + default: + return E_FAIL; + } + + hr = pDevice->CreateShaderResourceView( resource.Get(), &SRVDesc, ppSRV ); + if ( FAILED(hr) ) + return hr; + + assert( *ppSRV ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Save a texture resource to a DDS file in memory/on disk +//------------------------------------------------------------------------------------- +HRESULT CaptureTexture( ID3D11Device* pDevice, ID3D11DeviceContext* pContext, ID3D11Resource* pSource, ScratchImage& result ) +{ + if ( !pDevice || !pContext || !pSource ) + return E_INVALIDARG; + + D3D11_RESOURCE_DIMENSION resType = D3D11_RESOURCE_DIMENSION_UNKNOWN; + pSource->GetType( &resType ); + + HRESULT hr; + + switch( resType ) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + { + ScopedObject pTexture; + hr = pSource->QueryInterface( __uuidof(ID3D11Texture1D), (void**) &pTexture ); + if ( FAILED(hr) ) + break; + + assert( pTexture.Get() ); + + D3D11_TEXTURE1D_DESC desc; + pTexture->GetDesc( &desc ); + + desc.BindFlags = 0; + desc.MiscFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.Usage = D3D11_USAGE_STAGING; + + ScopedObject pStaging; + hr = pDevice->CreateTexture1D( &desc, 0, &pStaging ); + if ( FAILED(hr) ) + break; + + assert( pStaging.Get() ); + + pContext->CopyResource( pStaging.Get(), pSource ); + + TexMetadata mdata; + mdata.width = desc.Width; + mdata.height = mdata.depth = 1; + mdata.arraySize = desc.ArraySize; + mdata.mipLevels = desc.MipLevels; + mdata.miscFlags = 0; + mdata.format = desc.Format; + mdata.dimension = TEX_DIMENSION_TEXTURE1D; + + hr = result.Initialize( mdata ); + if ( FAILED(hr) ) + break; + + hr = _Capture( pContext, pStaging.Get(), mdata, result ); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + { + ScopedObject pTexture; + hr = pSource->QueryInterface( __uuidof(ID3D11Texture2D), (void**) &pTexture ); + if ( FAILED(hr) ) + break; + + assert( pTexture.Get() ); + + D3D11_TEXTURE2D_DESC desc; + pTexture->GetDesc( &desc ); + + ScopedObject pStaging; + if ( desc.SampleDesc.Count > 1 ) + { + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + + ScopedObject pTemp; + hr = pDevice->CreateTexture2D( &desc, 0, &pTemp ); + if ( FAILED(hr) ) + break; + + assert( pTemp.Get() ); + + for( UINT item = 0; item < desc.ArraySize; ++item ) + { + for( UINT level = 0; level < desc.MipLevels; ++level ) + { + UINT index = D3D11CalcSubresource( level, item, desc.MipLevels ); + pContext->ResolveSubresource( pTemp.Get(), index, pSource, index, desc.Format ); + } + } + + desc.BindFlags = 0; + desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.Usage = D3D11_USAGE_STAGING; + + hr = pDevice->CreateTexture2D( &desc, 0, &pStaging ); + if ( FAILED(hr) ) + break; + + assert( pStaging.Get() ); + + pContext->CopyResource( pStaging.Get(), pTemp.Get() ); + } + else + { + desc.BindFlags = 0; + desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.Usage = D3D11_USAGE_STAGING; + + hr = pDevice->CreateTexture2D( &desc, 0, &pStaging ); + if ( FAILED(hr) ) + break; + + assert( pStaging.Get() ); + + pContext->CopyResource( pStaging.Get(), pSource ); + } + + TexMetadata mdata; + mdata.width = desc.Width; + mdata.height = desc.Height; + mdata.depth = 1; + mdata.arraySize = desc.ArraySize; + mdata.mipLevels = desc.MipLevels; + mdata.miscFlags = (desc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) ? TEX_MISC_TEXTURECUBE : 0; + mdata.format = desc.Format; + mdata.dimension = TEX_DIMENSION_TEXTURE2D; + + hr = result.Initialize( mdata ); + if ( FAILED(hr) ) + break; + + hr = _Capture( pContext, pStaging.Get(), mdata, result ); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + { + ScopedObject pTexture; + hr = pSource->QueryInterface( __uuidof(ID3D11Texture3D), (void**) &pTexture ); + if ( FAILED(hr) ) + break; + + assert( pTexture.Get() ); + + D3D11_TEXTURE3D_DESC desc; + pTexture->GetDesc( &desc ); + + desc.BindFlags = 0; + desc.MiscFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.Usage = D3D11_USAGE_STAGING; + + ScopedObject pStaging; + hr = pDevice->CreateTexture3D( &desc, 0, &pStaging ); + if ( FAILED(hr) ) + break; + + assert( pStaging.Get() ); + + pContext->CopyResource( pStaging.Get(), pSource ); + + TexMetadata mdata; + mdata.width = desc.Width; + mdata.height = desc.Height; + mdata.depth = desc.Depth; + mdata.arraySize = 1; + mdata.mipLevels = desc.MipLevels; + mdata.miscFlags = 0; + mdata.format = desc.Format; + mdata.dimension = TEX_DIMENSION_TEXTURE3D; + + hr = result.Initialize( mdata ); + if ( FAILED(hr) ) + break; + + hr = _Capture( pContext, pStaging.Get(), mdata, result ); + } + break; + + default: + hr = E_FAIL; + break; + } + + if ( FAILED(hr) ) + { + result.Release(); + return hr; + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexDDS.cpp b/DirectXTex/DirectXTexDDS.cpp new file mode 100644 index 0000000..1c7f266 --- /dev/null +++ b/DirectXTex/DirectXTexDDS.cpp @@ -0,0 +1,1683 @@ +//------------------------------------------------------------------------------------- +// DirectXTexDDS.cpp +// +// DirectX Texture Library - Microsoft DirectDraw Surface (DDS) file format reader/writer +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +#include "dds.h" + +namespace DirectX +{ + +//------------------------------------------------------------------------------------- +// Legacy format mapping table (used for DDS files without 'DX10' extended header) +//------------------------------------------------------------------------------------- +enum CONVERSION_FLAGS +{ + CONV_FLAGS_NONE = 0x0, + CONV_FLAGS_EXPAND = 0x1, // Conversion requires expanded pixel size + CONV_FLAGS_NOALPHA = 0x2, // Conversion requires setting alpha to known value + CONV_FLAGS_SWIZZLE = 0x4, // BGR/RGB order swizzling required + CONV_FLAGS_PAL8 = 0x8, // Has an 8-bit palette + CONV_FLAGS_888 = 0x10, // Source is an 8:8:8 (24bpp) format + CONV_FLAGS_565 = 0x20, // Source is a 5:6:5 (16bpp) format + CONV_FLAGS_5551 = 0x40, // Source is a 5:5:5:1 (16bpp) format + CONV_FLAGS_4444 = 0x80, // Source is a 4:4:4:4 (16bpp) format + CONV_FLAGS_44 = 0x100, // Source is a 4:4 (8bpp) format + CONV_FLAGS_332 = 0x200, // Source is a 3:3:2 (8bpp) format + CONV_FLAGS_8332 = 0x400, // Source is a 8:3:3:2 (16bpp) format + CONV_FLAGS_A8P8 = 0x800, // Has an 8-bit palette with an alpha channel + CONV_FLAGS_DX10 = 0x10000, // Has the 'DX10' extension header +}; + +struct LegacyDDS +{ + DXGI_FORMAT format; + DWORD convFlags; + DDS_PIXELFORMAT ddpf; +}; + +const LegacyDDS g_LegacyDDSMap[] = +{ + { DXGI_FORMAT_BC1_UNORM, CONV_FLAGS_NONE, DDSPF_DXT1 }, // D3DFMT_DXT1 + { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_NONE, DDSPF_DXT3 }, // D3DFMT_DXT3 + { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, DDSPF_DXT5 }, // D3DFMT_DXT5 + + { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_NONE, DDSPF_DXT2 }, // D3DFMT_DXT2 (ignore premultiply) + { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, DDSPF_DXT4 }, // D3DFMT_DXT4 (ignore premultiply) + + { DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, DDSPF_BC4_UNORM }, + { DXGI_FORMAT_BC4_SNORM, CONV_FLAGS_NONE, DDSPF_BC4_SNORM }, + { DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, DDSPF_BC5_UNORM }, + { DXGI_FORMAT_BC5_SNORM, CONV_FLAGS_NONE, DDSPF_BC5_SNORM }, + + { DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC( 'A', 'T', 'I', '1' ), 0, 0, 0, 0, 0 } }, + { DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC( 'A', 'T', 'I', '2' ), 0, 0, 0, 0, 0 } }, + + { DXGI_FORMAT_R8G8_B8G8_UNORM, CONV_FLAGS_NONE, DDSPF_R8G8_B8G8 }, // D3DFMT_R8G8_B8G8 + { DXGI_FORMAT_G8R8_G8B8_UNORM, CONV_FLAGS_NONE, DDSPF_G8R8_G8B8 }, // D3DFMT_G8R8_G8B8 + + { DXGI_FORMAT_B8G8R8A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8R8G8B8 }, // D3DFMT_A8R8G8B8 (uses DXGI 1.1 format) + { DXGI_FORMAT_B8G8R8X8_UNORM, CONV_FLAGS_NONE, DDSPF_X8R8G8B8 }, // D3DFMT_X8R8G8B8 (uses DXGI 1.1 format) + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8B8G8R8 }, // D3DFMT_A8B8G8R8 + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_NOALPHA, DDSPF_X8B8G8R8 }, // D3DFMT_X8B8G8R8 + { DXGI_FORMAT_R16G16_UNORM, CONV_FLAGS_NONE, DDSPF_G16R16 }, // D3DFMT_G16R16 + + { DXGI_FORMAT_R10G10B10A2_UNORM, CONV_FLAGS_SWIZZLE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 } }, // D3DFMT_A2R10G10B10 (D3DX reversal issue workaround) + { DXGI_FORMAT_R10G10B10A2_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 } }, // D3DFMT_A2B10G10R10 (D3DX reversal issue workaround) + + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_NOALPHA + | CONV_FLAGS_888, DDSPF_R8G8B8 }, // D3DFMT_R8G8B8 + + { DXGI_FORMAT_B5G6R5_UNORM, CONV_FLAGS_565, DDSPF_R5G6B5 }, // D3DFMT_R5G6B5 + { DXGI_FORMAT_B5G5R5A1_UNORM, CONV_FLAGS_5551, DDSPF_A1R5G5B5 }, // D3DFMT_A1R5G5B5 + { DXGI_FORMAT_B5G5R5A1_UNORM, CONV_FLAGS_5551 + | CONV_FLAGS_NOALPHA, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x0000 } }, // D3DFMT_X1R5G5B5 + + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_8332, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x00e0, 0x001c, 0x0003, 0xff00 } }, // D3DFMT_A8R3G3B2 + { DXGI_FORMAT_B5G6R5_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_332, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0x00 } }, // D3DFMT_R3G3B2 + + { DXGI_FORMAT_R8_UNORM, CONV_FLAGS_NONE, DDSPF_L8 }, // D3DFMT_L8 + { DXGI_FORMAT_R16_UNORM, CONV_FLAGS_NONE, DDSPF_L16 }, // D3DFMT_L16 + { DXGI_FORMAT_R8G8_UNORM, CONV_FLAGS_NONE, DDSPF_A8L8 }, // D3DFMT_A8L8 + + { DXGI_FORMAT_A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8 }, // D3DFMT_A8 + + { DXGI_FORMAT_R16G16B16A16_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 36, 0, 0, 0, 0, 0 } }, // D3DFMT_A16B16G16R16 + { DXGI_FORMAT_R16G16B16A16_SNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 110, 0, 0, 0, 0, 0 } }, // D3DFMT_Q16W16V16U16 + { DXGI_FORMAT_R16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 111, 0, 0, 0, 0, 0 } }, // D3DFMT_R16F + { DXGI_FORMAT_R16G16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 112, 0, 0, 0, 0, 0 } }, // D3DFMT_G16R16F + { DXGI_FORMAT_R16G16B16A16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 113, 0, 0, 0, 0, 0 } }, // D3DFMT_A16B16G16R16F + { DXGI_FORMAT_R32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 114, 0, 0, 0, 0, 0 } }, // D3DFMT_R32F + { DXGI_FORMAT_R32G32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 115, 0, 0, 0, 0, 0 } }, // D3DFMT_G32R32F + { DXGI_FORMAT_R32G32B32A32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 116, 0, 0, 0, 0, 0 } }, // D3DFMT_A32B32G32R32F + + { DXGI_FORMAT_R32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0xffffffff, 0x00000000, 0x00000000, 0x00000000 } }, // D3DFMT_R32F (D3DX uses FourCC 114 instead) + + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_PAL8 + | CONV_FLAGS_A8P8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8, 0, 16, 0, 0, 0, 0 } }, // D3DFMT_A8P8 + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_PAL8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8, 0, 8, 0, 0, 0, 0 } }, // D3DFMT_P8 + +#ifdef DXGI_1_2_FORMATS + { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_4444, DDSPF_A4R4G4B4 }, // D3DFMT_A4R4G4B4 (uses DXGI 1.2 format) + { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_NOALPHA + | CONV_FLAGS_4444, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0x0000 } }, // D3DFMT_X4R4G4B4 (uses DXGI 1.2 format) + { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_44, { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0x0f, 0x00, 0x00, 0xf0 } }, // D3DFMT_A4L4 (uses DXGI 1.2 format) +#else // !DXGI_1_2_FORMATS + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_4444, DDSPF_A4R4G4B4 }, // D3DFMT_A4R4G4B4 + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_NOALPHA + | CONV_FLAGS_4444, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0x0000 } }, // D3DFMT_X4R4G4B4 + { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND + | CONV_FLAGS_44, { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0x0f, 0x00, 0x00, 0xf0 } }, // D3DFMT_A4L4 +#endif +}; + +// Note that many common DDS reader/writers (including D3DX) swap the +// the RED/BLUE masks for 10:10:10:2 formats. We assumme +// below that the 'backwards' header mask is being used since it is most +// likely written by D3DX. The more robust solution is to use the 'DX10' +// header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly + +// We do not support the following legacy Direct3D 9 formats: +// BumpDuDv D3DFMT_V8U8, D3DFMT_Q8W8V8U8, D3DFMT_V16U16, D3DFMT_A2W10V10U10 +// BumpLuminance D3DFMT_L6V5U5, D3DFMT_X8L8V8U8 +// FourCC "UYVY" D3DFMT_UYVY +// FourCC "YUY2" D3DFMT_YUY2 +// FourCC 117 D3DFMT_CxV8U8 +// ZBuffer D3DFMT_D16_LOCKABLE +// FourCC 82 D3DFMT_D32F_LOCKABLE + +static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _Inout_opt_ DWORD* convFlags ) +{ + const size_t MAP_SIZE = sizeof(g_LegacyDDSMap) / sizeof(LegacyDDS); + size_t index = 0; + for( index = 0; index < MAP_SIZE; ++index ) + { + const LegacyDDS* entry = &g_LegacyDDSMap[index]; + + if ( ddpf.dwFlags & entry->ddpf.dwFlags ) + { + if ( entry->ddpf.dwFlags & DDS_FOURCC ) + { + if ( ddpf.dwFourCC == entry->ddpf.dwFourCC ) + break; + } + else if ( entry->ddpf.dwFlags & DDS_PAL8 ) + { + if ( ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount ) + break; + } + else if ( ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount ) + { + // RGB, RGBA, ALPHA, LUMINANCE + if ( ddpf.dwRBitMask == entry->ddpf.dwRBitMask + && ddpf.dwGBitMask == entry->ddpf.dwGBitMask + && ddpf.dwBBitMask == entry->ddpf.dwBBitMask + && ddpf.dwABitMask == entry->ddpf.dwABitMask ) + break; + } + } + } + + if ( index >= MAP_SIZE ) + return DXGI_FORMAT_UNKNOWN; + + DWORD cflags = g_LegacyDDSMap[index].convFlags; + DXGI_FORMAT format = g_LegacyDDSMap[index].format; + + if ( (cflags & CONV_FLAGS_EXPAND) && (flags & DDS_FLAGS_NO_LEGACY_EXPANSION) ) + return DXGI_FORMAT_UNKNOWN; + + if ( (format == DXGI_FORMAT_R10G10B10A2_UNORM) && (flags & DDS_FLAGS_NO_R10B10G10A2_FIXUP) ) + { + cflags ^= CONV_FLAGS_SWIZZLE; + } + + if ( convFlags ) + *convFlags = cflags; + + return format; +} + + +//------------------------------------------------------------------------------------- +// Decodes DDS header including optional DX10 extended header +//------------------------------------------------------------------------------------- +static HRESULT _DecodeDDSHeader( _In_bytecount_(size) LPCVOID pSource, size_t size, DWORD flags, _Out_ TexMetadata& metadata, + _Inout_opt_ DWORD* convFlags ) +{ + if ( !pSource ) + return E_INVALIDARG; + + memset( &metadata, 0, sizeof(TexMetadata) ); + + if ( size < (sizeof(DDS_HEADER) + sizeof(uint32_t)) ) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + // DDS files always start with the same magic number ("DDS ") + uint32_t dwMagicNumber = *reinterpret_cast(pSource); + if ( dwMagicNumber != DDS_MAGIC ) + { + return E_FAIL; + } + + const DDS_HEADER* pHeader = reinterpret_cast( (const uint8_t*)pSource + sizeof( uint32_t ) ); + assert( pHeader ); + + // Verify header to validate DDS file + if ( pHeader->dwSize != sizeof(DDS_HEADER) + || pHeader->ddspf.dwSize != sizeof(DDS_PIXELFORMAT) ) + { + return E_FAIL; + } + + metadata.mipLevels = pHeader->dwMipMapCount; + if ( metadata.mipLevels == 0 ) + metadata.mipLevels = 1; + + // Check for DX10 extension + if ( (pHeader->ddspf.dwFlags & DDS_FOURCC) + && (MAKEFOURCC( 'D', 'X', '1', '0' ) == pHeader->ddspf.dwFourCC) ) + { + // Buffer must be big enough for both headers and magic value + if ( size < (sizeof(DDS_HEADER)+sizeof(uint32_t)+sizeof(DDS_HEADER_DXT10)) ) + { + return E_FAIL; + } + + const DDS_HEADER_DXT10* d3d10ext = reinterpret_cast( (const uint8_t*)pSource + sizeof( uint32_t ) + sizeof(DDS_HEADER) ); + if ( convFlags ) + *convFlags |= CONV_FLAGS_DX10; + + metadata.arraySize = d3d10ext->arraySize; + if ( metadata.arraySize == 0 ) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + metadata.format = d3d10ext->dxgiFormat; + if ( !IsValid( metadata.format ) ) + { + HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + switch ( d3d10ext->resourceDimension ) + { + case DDS_DIMENSION_TEXTURE1D: + + // D3DX writes 1D textures with a fixed Height of 1 + if ( (pHeader->dwFlags & DDS_HEIGHT) && pHeader->dwHeight != 1 ) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + metadata.width = pHeader->dwWidth; + metadata.height = 1; + metadata.depth = 1; + metadata.dimension = TEX_DIMENSION_TEXTURE1D; + break; + + case DDS_DIMENSION_TEXTURE2D: + if ( d3d10ext->miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE ) + { + metadata.miscFlags |= TEX_MISC_TEXTURECUBE; + metadata.arraySize *= 6; + } + + metadata.width = pHeader->dwWidth; + metadata.height = pHeader->dwHeight; + metadata.depth = 1; + metadata.dimension = TEX_DIMENSION_TEXTURE2D; + break; + + case DDS_DIMENSION_TEXTURE3D: + if ( !(pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME) ) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + if ( metadata.arraySize > 1 ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + metadata.width = pHeader->dwWidth; + metadata.height = pHeader->dwHeight; + metadata.depth = pHeader->dwDepth; + metadata.dimension = TEX_DIMENSION_TEXTURE3D; + break; + + default: + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + } + else + { + metadata.arraySize = 1; + + if ( pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME ) + { + metadata.width = pHeader->dwWidth; + metadata.height = pHeader->dwHeight; + metadata.depth = pHeader->dwDepth; + metadata.dimension = TEX_DIMENSION_TEXTURE3D; + } + else + { + if ( pHeader->dwCaps2 & DDS_CUBEMAP ) + { + // We require all six faces to be defined + if ( (pHeader->dwCaps2 & DDS_CUBEMAP_ALLFACES ) != DDS_CUBEMAP_ALLFACES ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + metadata.arraySize = 6; + metadata.miscFlags |= TEX_MISC_TEXTURECUBE; + } + + metadata.width = pHeader->dwWidth; + metadata.height = pHeader->dwHeight; + metadata.depth = 1; + metadata.dimension = TEX_DIMENSION_TEXTURE2D; + + // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture + } + + metadata.format = _GetDXGIFormat( pHeader->ddspf, flags, convFlags ); + + if ( metadata.format == DXGI_FORMAT_UNKNOWN ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + // Special flag for handling BGR DXGI 1.1 formats + if (flags & DDS_FLAGS_FORCE_RGB) + { + switch ( metadata.format ) + { + case DXGI_FORMAT_B8G8R8A8_UNORM: + metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; + if ( convFlags ) + *convFlags |= CONV_FLAGS_SWIZZLE; + break; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; + if ( convFlags ) + *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; + break; + + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS; + if ( convFlags ) + *convFlags |= CONV_FLAGS_SWIZZLE; + break; + + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + if ( convFlags ) + *convFlags |= CONV_FLAGS_SWIZZLE; + break; + + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS; + if ( convFlags ) + *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; + break; + + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + if ( convFlags ) + *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; + break; + } + } + + // Special flag for handling 16bpp formats + if (flags & DDS_FLAGS_NO_16BPP) + { + switch ( metadata.format ) + { + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: +#endif + metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; + if ( convFlags ) + { + *convFlags |= CONV_FLAGS_EXPAND; + if ( metadata.format == DXGI_FORMAT_B5G6R5_UNORM ) + *convFlags |= CONV_FLAGS_NOALPHA; + } + } + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Encodes DDS file header (magic value, header, optional DX10 extended header) +//------------------------------------------------------------------------------------- +HRESULT _EncodeDDSHeader( _In_ const TexMetadata& metadata, DWORD flags, + _Out_opt_cap_x_(maxsize) LPVOID pDestination, _In_ size_t maxsize, _Out_ size_t& required ) +{ + assert( IsValid( metadata.format ) && !IsVideo( metadata.format ) ); + + if ( metadata.arraySize > 1 ) + { + if ( (metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION_TEXTURE2D) || !(metadata.miscFlags & TEX_MISC_TEXTURECUBE) ) + { + flags |= DDS_FLAGS_FORCE_DX10_EXT; + } + } + + DDS_PIXELFORMAT ddpf = { 0 }; + if ( !(flags & DDS_FLAGS_FORCE_DX10_EXT) ) + { + switch( metadata.format ) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8B8G8R8, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_R16G16_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_G16R16, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_R8G8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8L8, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_R16_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_L16, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_R8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_L8, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_R8G8_B8G8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_R8G8_B8G8, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_G8R8_G8B8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC1_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC2_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT3, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC3_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT5, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC4_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_UNORM, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC4_SNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_SNORM, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC5_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC5_UNORM, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC5_SNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC5_SNORM, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_B5G6R5_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_R5G6B5, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_B5G5R5A1_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A1R5G5B5, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_B8G8R8A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.1 + case DXGI_FORMAT_B8G8R8X8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_X8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.1 + +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT) ); break; +#endif + + // Legacy D3DX formats using D3DFMT enum value as FourCC + case DXGI_FORMAT_R32G32B32A32_FLOAT: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 116; // D3DFMT_A32B32G32R32F + break; + case DXGI_FORMAT_R16G16B16A16_FLOAT: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 113; // D3DFMT_A16B16G16R16F + break; + case DXGI_FORMAT_R16G16B16A16_UNORM: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 36; // D3DFMT_A16B16G16R16 + break; + case DXGI_FORMAT_R16G16B16A16_SNORM: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 110; // D3DFMT_Q16W16V16U16 + break; + case DXGI_FORMAT_R32G32_FLOAT: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 115; // D3DFMT_G32R32F + break; + case DXGI_FORMAT_R16G16_FLOAT: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 112; // D3DFMT_G16R16F + break; + case DXGI_FORMAT_R32_FLOAT: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 114; // D3DFMT_R32F + break; + case DXGI_FORMAT_R16_FLOAT: + ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 111; // D3DFMT_R16F + break; + } + } + + required = sizeof(uint32_t) + sizeof(DDS_HEADER); + + if ( ddpf.dwSize == 0 ) + required += sizeof(DDS_HEADER_DXT10); + + if ( !pDestination ) + return S_OK; + + if ( maxsize < required ) + return E_NOT_SUFFICIENT_BUFFER; + + *reinterpret_cast(pDestination) = DDS_MAGIC; + + DDS_HEADER* header = reinterpret_cast( reinterpret_cast(pDestination) + sizeof(uint32_t) ); + assert( header ); + + memset( header, 0, sizeof(DDS_HEADER ) ); + header->dwSize = sizeof( DDS_HEADER ); + header->dwFlags = DDS_HEADER_FLAGS_TEXTURE; + header->dwCaps = DDS_SURFACE_FLAGS_TEXTURE; + + if (metadata.mipLevels > 0) + { + header->dwFlags |= DDS_HEADER_FLAGS_MIPMAP; + +#ifdef _AMD64_ + if ( metadata.mipLevels > 0xFFFFFFFF ) + return E_INVALIDARG; +#endif + + header->dwMipMapCount = static_cast( metadata.mipLevels ); + + if ( header->dwMipMapCount > 1 ) + header->dwCaps |= DDS_SURFACE_FLAGS_MIPMAP; + } + + switch( metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: +#ifdef _AMD64_ + if ( metadata.height > 0xFFFFFFFF ) + return E_INVALIDARG; +#endif + + header->dwHeight = static_cast( metadata.height ); + header->dwWidth = header->dwDepth = 1; + break; + + case TEX_DIMENSION_TEXTURE2D: +#ifdef _AMD64_ + if ( metadata.height > 0xFFFFFFFF + || metadata.width > 0xFFFFFFFF) + return E_INVALIDARG; +#endif + + header->dwHeight = static_cast( metadata.height ); + header->dwWidth = static_cast( metadata.width ); + header->dwDepth = 1; + + if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) + { + header->dwCaps |= DDS_SURFACE_FLAGS_CUBEMAP; + header->dwCaps2 |= DDS_CUBEMAP_ALLFACES; + } + break; + + case TEX_DIMENSION_TEXTURE3D: +#ifdef _AMD64_ + if ( metadata.height > 0xFFFFFFFF + || metadata.width > 0xFFFFFFFF + || metadata.depth > 0xFFFFFFFF ) + return E_INVALIDARG; +#endif + + header->dwFlags |= DDS_HEADER_FLAGS_VOLUME; + header->dwCaps2 |= DDS_FLAGS_VOLUME; + header->dwHeight = static_cast( metadata.height ); + header->dwWidth = static_cast( metadata.width ); + header->dwDepth = static_cast( metadata.depth ); + break; + + default: + return E_FAIL; + } + + size_t rowPitch, slicePitch; + ComputePitch( metadata.format, metadata.width, metadata.height, rowPitch, slicePitch, CP_FLAGS_NONE ); + +#ifdef _AMD64_ + if ( slicePitch > 0xFFFFFFFF + || rowPitch > 0xFFFFFFFF ) + return E_FAIL; +#endif + + if ( IsCompressed( metadata.format ) ) + { + header->dwFlags |= DDS_HEADER_FLAGS_LINEARSIZE; + header->dwPitchOrLinearSize = static_cast( slicePitch ); + } + else + { + header->dwFlags |= DDS_HEADER_FLAGS_PITCH; + header->dwPitchOrLinearSize = static_cast( rowPitch ); + } + + if ( ddpf.dwSize == 0 ) + { + memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) ); + + DDS_HEADER_DXT10* ext = reinterpret_cast( reinterpret_cast(header) + sizeof(DDS_HEADER) ); + assert( ext ); + + memset( ext, 0, sizeof(DDS_HEADER_DXT10) ); + ext->dxgiFormat = metadata.format; + ext->resourceDimension = metadata.dimension; + +#ifdef _AMD64_ + if ( metadata.arraySize > 0xFFFFFFFF ) + return E_INVALIDARG; +#endif + + if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) + { + ext->miscFlag |= TEX_MISC_TEXTURECUBE; + assert( (metadata.arraySize % 6) == 0 ); + ext->arraySize = static_cast( metadata.arraySize / 6 ); + } + else + { + ext->arraySize = static_cast( metadata.arraySize ); + } + } + else + { + memcpy_s( &header->ddspf, sizeof(header->ddspf), &ddpf, sizeof(ddpf) ); + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Converts an image row with optional clearing of alpha value to 1.0 +// Returns true if supported, false if expansion case not supported +//------------------------------------------------------------------------------------- +enum TEXP_LEGACY_FORMAT +{ + TEXP_LEGACY_UNKNOWN = 0, + TEXP_LEGACY_R8G8B8, + TEXP_LEGACY_R3G3B2, + TEXP_LEGACY_A8R3G3B2, + TEXP_LEGACY_P8, + TEXP_LEGACY_A8P8, + TEXP_LEGACY_A4L4, + TEXP_LEGACY_B4G4R4A4, +}; + +inline static TEXP_LEGACY_FORMAT _FindLegacyFormat( DWORD flags ) +{ + TEXP_LEGACY_FORMAT lformat = TEXP_LEGACY_UNKNOWN; + + if ( flags & CONV_FLAGS_PAL8 ) + { + lformat = ( flags & CONV_FLAGS_A8P8 ) ? TEXP_LEGACY_A8P8 : TEXP_LEGACY_P8; + } + else if ( flags & CONV_FLAGS_888 ) + lformat = TEXP_LEGACY_R8G8B8; + else if ( flags & CONV_FLAGS_332 ) + lformat = TEXP_LEGACY_R3G3B2; + else if ( flags & CONV_FLAGS_8332 ) + lformat = TEXP_LEGACY_A8R3G3B2; + else if ( flags & CONV_FLAGS_44 ) + lformat = TEXP_LEGACY_A4L4; +#ifndef DXGI_1_2_FORMATS + else if ( flags & CONV_FLAGS_4444 ) + lformat = TEXP_LEGACY_B4G4R4A4; +#endif + + return lformat; +} + +static bool _LegacyExpandScanline( _Out_bytecap_(outSize) LPVOID pDestination, size_t outSize, _In_ DXGI_FORMAT outFormat, + _In_bytecount_(inSize) LPCVOID pSource, size_t inSize, _In_ TEXP_LEGACY_FORMAT inFormat, + _In_opt_count_c_(256) const uint32_t* pal8, _In_ DWORD flags ) +{ + assert( pDestination && outSize > 0 ); + assert( pSource && inSize > 0 ); + assert( IsValid(outFormat) && !IsVideo(outFormat) ); + + switch( inFormat ) + { + case TEXP_LEGACY_R8G8B8: + if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM ) + return false; + + // D3DFMT_R8G8B8 -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint8_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 3, ocount += 4 ) + { + uint32_t t1 = *sPtr; + uint32_t t2 = ( *(sPtr+1) << 8 ); + uint32_t t3 = ( *(sPtr+2) << 16 ); + + *(dPtr++) = t1 | t2 | t3 | 0xff000000; + sPtr += 3; + } + } + return true; + + case TEXP_LEGACY_R3G3B2: + switch( outFormat ) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + // D3DFMT_R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint8_t* __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 ) + { + uint8_t t = *(sPtr++); + + uint32_t t1 = (t & 0xe0) | ((t & 0xe0) >> 3) | ((t & 0xc0) >> 6); + uint32_t t2 = ((t & 0x1c) << 11) | ((t & 0x1c) << 8) | ((t & 0x18) << 5); + uint32_t t3 = ((t & 0x03) << 22) | ((t & 0x03) << 20) | ((t & 0x03) << 18) | ((t & 0x03) << 16); + + *(dPtr++) = t1 | t2 | t3 | 0xff000000; + } + } + return true; + + case DXGI_FORMAT_B5G6R5_UNORM: + // D3DFMT_R3G3B2 -> DXGI_FORMAT_B5G6R5_UNORM + { + const uint8_t* __restrict sPtr = reinterpret_cast(pSource); + uint16_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 2 ) + { + uint8_t t = *(sPtr++); + + uint16_t t1 = ((t & 0xe0) << 8) | ((t & 0xc0) << 5); + uint16_t t2 = ((t & 0x1c) << 6) | ((t & 0x1c) << 3); + uint16_t t3 = ((t & 0x03) << 3) | ((t & 0x03) << 1) | ((t & 0x02) >> 1); + + *(dPtr++) = t1 | t2 | t3; + } + } + return true; + } + break; + + case TEXP_LEGACY_A8R3G3B2: + if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM ) + return false; + + // D3DFMT_A8R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint16_t* __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 ) + { + uint16_t t = *(sPtr++); + + uint32_t t1 = (t & 0x00e0) | ((t & 0x00e0) >> 3) | ((t & 0x00c0) >> 6); + uint32_t t2 = ((t & 0x001c) << 11) | ((t & 0x001c) << 8) | ((t & 0x0018) << 5); + uint32_t t3 = ((t & 0x0003) << 22) | ((t & 0x0003) << 20) | ((t & 0x0003) << 18) | ((t & 0x0003) << 16); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + return true; + + case TEXP_LEGACY_P8: + if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 ) + return false; + + // D3DFMT_P8 -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint8_t* __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 ) + { + uint8_t t = *(sPtr++); + + *(dPtr++) = pal8[ t ]; + } + } + return true; + + case TEXP_LEGACY_A8P8: + if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 ) + return false; + + // D3DFMT_A8P8 -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint16_t* __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 ) + { + uint16_t t = *(sPtr++); + + uint32_t t1 = pal8[ t & 0xff ]; + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16); + + *(dPtr++) = t1 | ta; + } + } + return true; + + case TEXP_LEGACY_A4L4: + switch( outFormat ) + { +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM : + // D3DFMT_A4L4 -> DXGI_FORMAT_B4G4R4A4_UNORM + { + const uint8_t * __restrict sPtr = reinterpret_cast(pSource); + uint16_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 2 ) + { + uint8_t t = *(sPtr++); + + uint16_t t1 = (t & 0x0f); + uint16_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xf000 : ((t & 0xf0) << 8); + + *(dPtr++) = t1 | (t1 << 4) | (t1 << 8) | ta; + } + } + return true; +#endif // DXGI_1_2_FORMATS + + case DXGI_FORMAT_R8G8B8A8_UNORM: + // D3DFMT_A4L4 -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint8_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 ) + { + uint8_t t = *(sPtr++); + + uint32_t t1 = ((t & 0x0f) << 4) | (t & 0x0f); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf0) << 24) | ((t & 0xf0) << 20)); + + *(dPtr++) = t1 | (t1 << 8) | (t1 << 16) | ta; + } + } + return true; + } + break; + +#ifndef DXGI_1_2_FORMATS + case TEXP_LEGACY_B4G4R4A4: + if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) + return false; + + // D3DFMT_A4R4G4B4 -> DXGI_FORMAT_R8G8B8A8_UNORM + { + const uint16_t * __restrict sPtr = reinterpret_cast(pSource); + uint32_t * __restrict dPtr = reinterpret_cast(pDestination); + + for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 ) + { + uint16_t t = *(sPtr++); + + uint32_t t1 = ((t & 0x0f00) >> 4) | ((t & 0x0f00) >> 8); + uint32_t t2 = ((t & 0x00f0) << 8) | ((t & 0x00f0) << 4); + uint32_t t3 = ((t & 0x000f) << 20) | ((t & 0x000f) << 16); + uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf000) << 16) | ((t & 0xf000) << 12)); + + *(dPtr++) = t1 | t2 | t3 | ta; + } + } + return true; +#endif + } + + return false; +} + + +//------------------------------------------------------------------------------------- +// Converts or copies image data from pPixels into scratch image data +//------------------------------------------------------------------------------------- +static HRESULT _CopyImage( _In_bytecount_(size) const void* pPixels, _In_ size_t size, + _In_ const TexMetadata& metadata, _In_ DWORD cpFlags, _In_ DWORD convFlags, _In_opt_count_c_(256) const uint32_t *pal8, _In_ const ScratchImage& image ) +{ + assert( pPixels ); + assert( image.GetPixels() ); + + if ( !size ) + return E_FAIL; + + if ( convFlags & CONV_FLAGS_EXPAND ) + { + if ( convFlags & CONV_FLAGS_888 ) + cpFlags |= CP_FLAGS_24BPP; + else if ( convFlags & (CONV_FLAGS_565 | CONV_FLAGS_5551 | CONV_FLAGS_4444 | CONV_FLAGS_8332 | CONV_FLAGS_A8P8 ) ) + cpFlags |= CP_FLAGS_16BPP; + else if ( convFlags & (CONV_FLAGS_44 | CONV_FLAGS_332 | CONV_FLAGS_PAL8) ) + cpFlags |= CP_FLAGS_8BPP; + } + + size_t pixelSize, nimages; + _DetermineImageArray( metadata, cpFlags, nimages, pixelSize ); + if ( (nimages == 0) || (nimages != image.GetImageCount()) ) + { + return E_FAIL; + } + + assert( pixelSize <= size ); + + std::unique_ptr timages( new Image[nimages] ); + if ( !_SetupImageArray( (uint8_t*)pPixels, size, metadata, cpFlags, timages.get(), nimages ) ) + { + return E_FAIL; + } + + if ( nimages != image.GetImageCount() ) + { + return E_FAIL; + } + + const Image* images = image.GetImages(); + if ( !images ) + { + return E_FAIL; + } + + DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0; + if ( convFlags & CONV_FLAGS_SWIZZLE ) + tflags |= TEXP_SCANLINE_LEGACY; + + switch (metadata.dimension) + { + case TEX_DIMENSION_TEXTURE1D: + case TEX_DIMENSION_TEXTURE2D: + { + size_t index = 0; + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + for( size_t level = 0; level < metadata.mipLevels; ++level, ++index ) + { + if ( index >= nimages ) + return E_FAIL; + + if ( images[ index ].height != timages[ index ].height ) + return E_FAIL; + + size_t dpitch = images[ index ].rowPitch; + size_t spitch = timages[ index ].rowPitch; + + const uint8_t *pSrc = const_cast( timages[ index ].pixels ); + if ( !pSrc ) + return E_POINTER; + + uint8_t *pDest = images[ index ].pixels; + if ( !pDest ) + return E_POINTER; + + if ( IsCompressed( metadata.format ) ) + { + size_t csize = std::min( images[ index ].slicePitch, timages[ index ].slicePitch ); + memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize ); + } + else + { + for( size_t h = 0; h < images[ index ].height; ++h ) + { + if ( convFlags & CONV_FLAGS_EXPAND ) + { +#ifdef DXGI_1_2_FORMATS + if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) ) +#else + if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551) ) +#endif + { + if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM, + pSrc, spitch, + (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM, + tflags ) ) + return E_FAIL; + } + else + { + TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags ); + if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format, + pSrc, spitch, lformat, pal8, + tflags ) ) + return E_FAIL; + } + } + else if ( convFlags & CONV_FLAGS_SWIZZLE ) + { + _SwizzleScanline( pDest, dpitch, pSrc, spitch, + metadata.format, tflags ); + } + else + { + _CopyScanline( pDest, dpitch, pSrc, spitch, + metadata.format, tflags ); + } + + pSrc += spitch; + pDest += dpitch; + } + } + } + } + } + break; + + case TEX_DIMENSION_TEXTURE3D: + { + size_t index = 0; + size_t d = metadata.depth; + + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + for( size_t slice = 0; slice < d; ++slice, ++index ) + { + if ( index >= nimages ) + return E_FAIL; + + if ( images[ index ].height != timages[ index ].height ) + return E_FAIL; + + size_t dpitch = images[ index ].rowPitch; + size_t spitch = timages[ index ].rowPitch; + + const uint8_t *pSrc = const_cast( timages[ index ].pixels ); + if ( !pSrc ) + return E_POINTER; + + uint8_t *pDest = images[ index ].pixels; + if ( !pDest ) + return E_POINTER; + + if ( IsCompressed( metadata.format ) ) + { + size_t csize = std::min( images[ index ].slicePitch, timages[ index ].slicePitch ); + memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize ); + } + else + { + for( size_t h = 0; h < images[ index ].height; ++h ) + { + if ( convFlags & CONV_FLAGS_EXPAND ) + { +#ifdef DXGI_1_2_FORMATS + if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) ) +#else + if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551) ) +#endif + { + if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM, + pSrc, spitch, + (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM, + tflags ) ) + return E_FAIL; + } + else + { + TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags ); + if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format, + pSrc, spitch, lformat, pal8, + tflags ) ) + return E_FAIL; + } + } + else if ( convFlags & CONV_FLAGS_SWIZZLE ) + { + _SwizzleScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags ); + } + else + { + _CopyScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags ); + } + + pSrc += spitch; + pDest += dpitch; + } + } + } + + if ( d > 1 ) + d >>= 1; + } + } + break; + + default: + return E_FAIL; + } + + return S_OK; +} + +static HRESULT _CopyImageInPlace( DWORD convFlags, _In_ const ScratchImage& image ) +{ + if ( !image.GetPixels() ) + return E_FAIL; + + const Image* images = image.GetImages(); + if ( !images ) + return E_FAIL; + + const TexMetadata& metadata = image.GetMetadata(); + + DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0; + if ( convFlags & CONV_FLAGS_SWIZZLE ) + tflags |= TEXP_SCANLINE_LEGACY; + + for( size_t i = 0; i < image.GetImageCount(); ++i ) + { + const Image* img = &images[ i ]; + uint8_t *pPixels = img->pixels; + if ( !pPixels ) + return E_POINTER; + + size_t rowPitch = img->rowPitch; + + for( size_t h = 0; h < img->height; ++h ) + { + if ( convFlags & CONV_FLAGS_SWIZZLE ) + { + _SwizzleScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags ); + } + else + { + _CopyScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags ); + } + + pPixels += rowPitch; + } + } + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Obtain metadata from DDS file in memory/on disk +//------------------------------------------------------------------------------------- + +HRESULT GetMetadataFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata& metadata ) +{ + if ( !pSource || size == 0 ) + return E_INVALIDARG; + + return _DecodeDDSHeader( pSource, size, flags, metadata, 0 ); +} + +HRESULT GetMetadataFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata& metadata ) +{ + if ( !szFile ) + return E_INVALIDARG; + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) ); +#else + ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) ); +#endif + if ( !hFile ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + // Get the file size + LARGE_INTEGER fileSize = {0}; + +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) + FILE_STANDARD_INFO fileInfo; + if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + fileSize = fileInfo.EndOfFile; +#else + if ( !GetFileSizeEx( hFile.get(), &fileSize ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } +#endif + + // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid DDS file) + if ( fileSize.HighPart > 0 ) + { + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); + } + + // Need at least enough data to fill the standard header and magic number to be a valid DDS + if ( fileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) ) + { + return E_FAIL; + } + + // Read the header in (including extended header if present) + const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); + uint8_t header[MAX_HEADER_SIZE]; + + DWORD bytesRead = 0; + if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + return _DecodeDDSHeader( header, bytesRead, flags, metadata, 0 ); +} + + +//------------------------------------------------------------------------------------- +// Load a DDS file in memory +//------------------------------------------------------------------------------------- +HRESULT LoadFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata* metadata, ScratchImage& image ) +{ + if ( !pSource || size == 0 ) + return E_INVALIDARG; + + image.Release(); + + DWORD convFlags = 0; + TexMetadata mdata; + HRESULT hr = _DecodeDDSHeader( pSource, size, flags, mdata, &convFlags ); + if ( FAILED(hr) ) + return hr; + + size_t offset = sizeof(uint32_t) + sizeof(DDS_HEADER); + if ( convFlags & CONV_FLAGS_DX10 ) + offset += sizeof(DDS_HEADER_DXT10); + + assert( offset <= size ); + + const uint32_t *pal8 = nullptr; + if ( convFlags & CONV_FLAGS_PAL8 ) + { + pal8 = reinterpret_cast( reinterpret_cast(pSource) + offset ); + assert( pal8 ); + offset += ( 256 * sizeof(uint32_t) ); + if ( size < offset ) + return E_FAIL; + } + + hr = image.Initialize( mdata ); + if ( FAILED(hr) ) + return hr; + + LPCVOID pPixels = reinterpret_cast( reinterpret_cast(pSource) + offset ); + assert( pPixels ); + hr = _CopyImage( pPixels, size - offset, mdata, + (flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE, convFlags, pal8, image ); + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + if ( metadata ) + memcpy( metadata, &mdata, sizeof(TexMetadata) ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Load a DDS file from disk +//------------------------------------------------------------------------------------- +HRESULT LoadFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, ScratchImage& image ) +{ + if ( !szFile ) + return E_INVALIDARG; + + image.Release(); + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + ScopedHandle hFile( safe_handle ( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) ); +#else + ScopedHandle hFile( safe_handle ( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) ); +#endif + + if ( !hFile ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + // Get the file size + LARGE_INTEGER fileSize = {0}; + +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) + FILE_STANDARD_INFO fileInfo; + if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + fileSize = fileInfo.EndOfFile; +#else + if ( !GetFileSizeEx( hFile.get(), &fileSize ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } +#endif + + // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid DDS file) + if ( fileSize.HighPart > 0 ) + { + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); + } + + // Need at least enough data to fill the standard header and magic number to be a valid DDS + if ( fileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) ) + { + return E_FAIL; + } + + // Read the header in (including extended header if present) + const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); + uint8_t header[MAX_HEADER_SIZE]; + + DWORD bytesRead = 0; + if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + DWORD convFlags = 0; + TexMetadata mdata; + HRESULT hr = _DecodeDDSHeader( header, bytesRead, flags, mdata, &convFlags ); + if ( FAILED(hr) ) + return hr; + + DWORD offset = MAX_HEADER_SIZE; + + if ( !(convFlags & CONV_FLAGS_DX10) ) + { + // Must reset file position since we read more than the standard header above + LARGE_INTEGER filePos = { sizeof(uint32_t) + sizeof(DDS_HEADER), 0}; + if ( !SetFilePointerEx( hFile.get(), filePos, 0, FILE_BEGIN ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + offset = sizeof(uint32_t) + sizeof(DDS_HEADER); + } + + std::unique_ptr pal8; + if ( convFlags & CONV_FLAGS_PAL8 ) + { + pal8.reset( new uint32_t[256] ); + if ( !pal8 ) + { + return E_OUTOFMEMORY; + } + + if ( !ReadFile( hFile.get(), pal8.get(), 256 * sizeof(uint32_t), &bytesRead, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesRead != (256 * sizeof(uint32_t)) ) + { + return E_FAIL; + } + + offset += ( 256 * sizeof(uint32_t) ); + } + + DWORD remaining = fileSize.LowPart - offset; + if ( remaining == 0 ) + return E_FAIL; + + hr = image.Initialize( mdata ); + if ( FAILED(hr) ) + return hr; + + if ( (convFlags & CONV_FLAGS_EXPAND) || (flags & DDS_FLAGS_LEGACY_DWORD) ) + { + std::unique_ptr temp( new uint8_t[ remaining ] ); + if ( !temp ) + { + image.Release(); + return E_OUTOFMEMORY; + } + + if ( !ReadFile( hFile.get(), temp.get(), remaining, &bytesRead, 0 ) ) + { + image.Release(); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesRead != remaining ) + { + image.Release(); + return E_FAIL; + } + + hr = _CopyImage( temp.get(), remaining, mdata, + (flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE, + convFlags, pal8.get(), image ); + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + } + else + { + if ( remaining > image.GetPixelsSize() ) + { + image.Release(); + return E_FAIL; + } + + if ( !ReadFile( hFile.get(), image.GetPixels(), static_cast( image.GetPixelsSize() ), &bytesRead, 0 ) ) + { + image.Release(); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( convFlags & (CONV_FLAGS_SWIZZLE|CONV_FLAGS_NOALPHA) ) + { + // Swizzle/copy image in place + hr = _CopyImageInPlace( convFlags, image ); + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + } + } + + if ( metadata ) + memcpy( metadata, &mdata, sizeof(TexMetadata) ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Save a DDS file to memory +//------------------------------------------------------------------------------------- +HRESULT SaveToDDSMemory( const Image* images, size_t nimages, const TexMetadata& metadata, DWORD flags, Blob& blob ) +{ + if ( !images || (nimages == 0) ) + return E_INVALIDARG; + + // Determine memory required + size_t required = 0; + HRESULT hr = _EncodeDDSHeader( metadata, flags, 0, 0, required ); + if ( FAILED(hr) ) + return hr; + + for( size_t i = 0; i < nimages; ++i ) + { + required += images[ i ].slicePitch; + if ( !images[ i ].pixels ) + return E_POINTER; + } + + assert( required > 0 ); + + blob.Release(); + + hr = blob.Initialize( required ); + if ( FAILED(hr) ) + return hr; + + uint8_t* pDestination = reinterpret_cast( blob.GetBufferPointer() ); + assert( pDestination ); + + hr = _EncodeDDSHeader( metadata, flags, pDestination, blob.GetBufferSize(), required ); + if ( FAILED(hr) ) + { + blob.Release(); + return hr; + } + + size_t remaining = blob.GetBufferSize() - required; + pDestination += required; + + if ( !remaining ) + { + blob.Release(); + return E_FAIL; + } + + switch( metadata.dimension ) + { + case DDS_DIMENSION_TEXTURE1D: + case DDS_DIMENSION_TEXTURE2D: + { + size_t index = 0; + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + if ( index >= nimages ) + { + blob.Release(); + return E_FAIL; + } + + size_t pixsize = images[ index ].slicePitch; + if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) ) + { + blob.Release(); + return E_FAIL; + } + pDestination += pixsize; + remaining -= pixsize; + + ++index; + } + } + } + break; + + case DDS_DIMENSION_TEXTURE3D: + { + if ( metadata.arraySize != 1 ) + { + blob.Release(); + return E_FAIL; + } + + size_t d = metadata.depth; + + size_t index = 0; + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + for( size_t slice = 0; slice < d; ++slice ) + { + if ( index >= nimages ) + { + blob.Release(); + return E_FAIL; + } + + size_t pixsize = images[ index ].slicePitch; + if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) ) + { + blob.Release(); + return E_FAIL; + } + pDestination += pixsize; + remaining -= pixsize; + + ++index; + } + + if ( d > 1 ) + d >>= 1; + } + } + break; + + default: + blob.Release(); + return E_FAIL; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Save a DDS file to disk +//------------------------------------------------------------------------------------- +HRESULT SaveToDDSFile( const Image* images, size_t nimages, const TexMetadata& metadata, DWORD flags, LPCWSTR szFile ) +{ + if ( !szFile ) + return E_INVALIDARG; + + // Create DDS Header + const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10); + uint8_t header[MAX_HEADER_SIZE]; + size_t required; + HRESULT hr = _EncodeDDSHeader( metadata, flags, header, MAX_HEADER_SIZE, required ); + if ( FAILED(hr) ) + return hr; + + // Create file and write header +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, 0 ) ) ); +#else + ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 ) ) ); +#endif + if ( !hFile ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + DWORD bytesWritten; + if ( !WriteFile( hFile.get(), header, static_cast( required ), &bytesWritten, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesWritten != required ) + { + return E_FAIL; + } + + // Write images + switch( metadata.dimension ) + { + case DDS_DIMENSION_TEXTURE1D: + case DDS_DIMENSION_TEXTURE2D: + { + size_t index = 0; + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + for( size_t level = 0; level < metadata.mipLevels; ++level, ++index ) + { + if ( index >= nimages ) + return E_FAIL; + + if ( !images[ index ].pixels ) + return E_POINTER; + + size_t pixsize = images[ index ].slicePitch; + + if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast( pixsize ), &bytesWritten, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesWritten != pixsize ) + { + return E_FAIL; + } + } + } + } + break; + + case DDS_DIMENSION_TEXTURE3D: + { + if ( metadata.arraySize != 1 ) + return E_FAIL; + + size_t d = metadata.depth; + + size_t index = 0; + for( size_t level = 0; level < metadata.mipLevels; ++level ) + { + for( size_t slice = 0; slice < d; ++slice, ++index ) + { + if ( index >= nimages ) + return E_FAIL; + + if ( !images[ index ].pixels ) + return E_POINTER; + + size_t pixsize = images[ index ].slicePitch; + + if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast( pixsize ), &bytesWritten, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesWritten != pixsize ) + { + return E_FAIL; + } + } + + if ( d > 1 ) + d >>= 1; + } + } + break; + + default: + return E_FAIL; + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexFlipRotate.cpp b/DirectXTex/DirectXTexFlipRotate.cpp new file mode 100644 index 0000000..34414fb --- /dev/null +++ b/DirectXTex/DirectXTexFlipRotate.cpp @@ -0,0 +1,327 @@ +//------------------------------------------------------------------------------------- +// DirectXTexFlipRotate.cpp +// +// DirectX Texture Library - Image flip/rotate operations +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +namespace DirectX +{ + +//------------------------------------------------------------------------------------- +// Do flip/rotate operation using WIC +//------------------------------------------------------------------------------------- +static HRESULT _PerformFlipRotateUsingWIC( _In_ const Image& srcImage, _In_ DWORD flags, + _In_ const WICPixelFormatGUID& pfGUID, _In_ const Image& destImage ) +{ + if ( !srcImage.pixels || !destImage.pixels ) + return E_POINTER; + + assert( srcImage.format == destImage.format ); + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject source; + HRESULT hr = pWIC->CreateBitmapFromMemory( static_cast( srcImage.width ), static_cast( srcImage.height ), pfGUID, + static_cast( srcImage.rowPitch ), static_cast( srcImage.slicePitch ), + srcImage.pixels, &source ); + if ( FAILED(hr) ) + return hr; + + ScopedObject FR; + hr = pWIC->CreateBitmapFlipRotator( &FR ); + if ( FAILED(hr) ) + return hr; + + hr = FR->Initialize( source.Get(), static_cast( flags ) ); + if ( FAILED(hr) ) + return hr; + + WICPixelFormatGUID pfFR; + hr = FR->GetPixelFormat( &pfFR ); + if ( FAILED(hr) ) + return hr; + + if ( memcmp( &pfFR, &pfGUID, sizeof(GUID) ) != 0 ) + { + // Flip/rotate should return the same format as the source... + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + UINT nwidth, nheight; + hr = FR->GetSize( &nwidth, &nheight ); + if ( FAILED(hr) ) + return hr; + + if ( destImage.width != nwidth || destImage.height != nheight ) + return E_FAIL; + + hr = FR->CopyPixels( 0, static_cast( destImage.rowPitch ), static_cast( destImage.slicePitch ), destImage.pixels ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Do conversion, flip/rotate using WIC, conversion cycle +//------------------------------------------------------------------------------------- +static HRESULT _PerformFlipRotateViaF32( _In_ const Image& srcImage, _In_ DWORD flags, _In_ const Image& destImage ) +{ + if ( !srcImage.pixels || !destImage.pixels ) + return E_POINTER; + + assert( srcImage.format != DXGI_FORMAT_R32G32B32A32_FLOAT ); + assert( srcImage.format == destImage.format ); + + ScratchImage temp; + HRESULT hr = _ConvertToR32G32B32A32( srcImage, temp ); + if ( FAILED(hr) ) + return hr; + + const Image *tsrc = temp.GetImage( 0, 0, 0 ); + if ( !tsrc ) + return E_POINTER; + + ScratchImage rtemp; + hr = rtemp.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, destImage.width, destImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *tdest = rtemp.GetImage( 0, 0, 0 ); + if ( !tdest ) + return E_POINTER; + + hr = _PerformFlipRotateUsingWIC( *tsrc, flags, GUID_WICPixelFormat128bppRGBAFloat, *tdest ); + if ( FAILED(hr) ) + return hr; + + temp.Release(); + + hr = _ConvertFromR32G32B32A32( *tdest, destImage ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Flip/rotate image +//------------------------------------------------------------------------------------- +HRESULT FlipRotate( const Image& srcImage, DWORD flags, ScratchImage& image ) +{ + if ( !srcImage.pixels ) + return E_POINTER; + + if ( !flags ) + return E_INVALIDARG; + +#ifdef _AMD64_ + if ( (srcImage.width > 0xFFFFFFFF) || (srcImage.height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + if ( IsCompressed( srcImage.format ) ) + { + // We don't support flip/rotate operations on compressed images + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + static_assert( TEX_FR_ROTATE0 == WICBitmapTransformRotate0, "TEX_FR_ROTATE0 no longer matches WIC" ); + static_assert( TEX_FR_ROTATE90 == WICBitmapTransformRotate90, "TEX_FR_ROTATE90 no longer matches WIC" ); + static_assert( TEX_FR_ROTATE180 == WICBitmapTransformRotate180, "TEX_FR_ROTATE180 no longer matches WIC" ); + static_assert( TEX_FR_ROTATE270 == WICBitmapTransformRotate270, "TEX_FR_ROTATE270 no longer matches WIC" ); + static_assert( TEX_FR_FLIP_HORIZONTAL == WICBitmapTransformFlipHorizontal, "TEX_FR_FLIP_HORIZONTAL no longer matches WIC" ); + static_assert( TEX_FR_FLIP_VERTICAL == WICBitmapTransformFlipVertical, "TEX_FR_FLIP_VERTICAL no longer matches WIC" ); + + // Only supports 90, 180, 270, or no rotation flags... not a combination of rotation flags + switch ( flags & (TEX_FR_ROTATE90|TEX_FR_ROTATE180|TEX_FR_ROTATE270) ) + { + case 0: + case TEX_FR_ROTATE90: + case TEX_FR_ROTATE180: + case TEX_FR_ROTATE270: + break; + + default: + return E_INVALIDARG; + } + + size_t nwidth = srcImage.width; + size_t nheight = srcImage.height; + + if (flags & (TEX_FR_ROTATE90|TEX_FR_ROTATE270)) + { + nwidth = srcImage.height; + nheight = srcImage.width; + } + + HRESULT hr = image.Initialize2D( srcImage.format, nwidth, nheight, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *rimage = image.GetImage( 0, 0, 0 ); + if ( !rimage ) + return E_POINTER; + + WICPixelFormatGUID pfGUID; + if ( _DXGIToWIC( srcImage.format, pfGUID ) ) + { + // Case 1: Source format is supported by Windows Imaging Component + hr = _PerformFlipRotateUsingWIC( srcImage, flags, pfGUID, *rimage ); + } + else + { + // Case 2: Source format is not supported by WIC, so we have to convert, flip/rotate, and convert back + hr = _PerformFlipRotateViaF32( srcImage, flags, *rimage ); + } + + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Flip/rotate image (complex) +//------------------------------------------------------------------------------------- +HRESULT FlipRotate( const Image* srcImages, size_t nimages, const TexMetadata& metadata, + DWORD flags, ScratchImage& result ) +{ + if ( !srcImages || !nimages ) + return E_INVALIDARG; + + if ( IsCompressed( metadata.format ) ) + { + // We don't support flip/rotate operations on compressed images + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + static_assert( TEX_FR_ROTATE0 == WICBitmapTransformRotate0, "TEX_FR_ROTATE0 no longer matches WIC" ); + static_assert( TEX_FR_ROTATE90 == WICBitmapTransformRotate90, "TEX_FR_ROTATE90 no longer matches WIC" ); + static_assert( TEX_FR_ROTATE180 == WICBitmapTransformRotate180, "TEX_FR_ROTATE180 no longer matches WIC" ); + static_assert( TEX_FR_ROTATE270 == WICBitmapTransformRotate270, "TEX_FR_ROTATE270 no longer matches WIC" ); + static_assert( TEX_FR_FLIP_HORIZONTAL == WICBitmapTransformFlipHorizontal, "TEX_FR_FLIP_HORIZONTAL no longer matches WIC" ); + static_assert( TEX_FR_FLIP_VERTICAL == WICBitmapTransformFlipVertical, "TEX_FR_FLIP_VERTICAL no longer matches WIC" ); + + // Only supports 90, 180, 270, or no rotation flags... not a combination of rotation flags + switch ( flags & (TEX_FR_ROTATE90|TEX_FR_ROTATE180|TEX_FR_ROTATE270) ) + { + case 0: + case TEX_FR_ROTATE90: + case TEX_FR_ROTATE180: + case TEX_FR_ROTATE270: + break; + + default: + return E_INVALIDARG; + } + + TexMetadata mdata2 = metadata; + + bool flipwh = false; + if (flags & (TEX_FR_ROTATE90|TEX_FR_ROTATE270)) + { + flipwh = true; + mdata2.width = metadata.height; + mdata2.height = metadata.width; + } + + HRESULT hr = result.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + if ( nimages != result.GetImageCount() ) + { + result.Release(); + return E_FAIL; + } + + const Image* dest = result.GetImages(); + if ( !dest ) + { + result.Release(); + return E_POINTER; + } + + WICPixelFormatGUID pfGUID; + bool wicpf = _DXGIToWIC( metadata.format, pfGUID ); + + for( size_t index=0; index < nimages; ++index ) + { + const Image& src = srcImages[ index ]; + if ( src.format != metadata.format ) + { + result.Release(); + return E_FAIL; + } + +#ifdef _AMD64_ + if ( (src.width > 0xFFFFFFFF) || (src.height > 0xFFFFFFFF) ) + return E_FAIL; +#endif + + const Image& dst = dest[ index ]; + assert( dst.format == metadata.format ); + + if ( flipwh ) + { + if ( src.width != dst.height || src.height != dst.width ) + { + result.Release(); + return E_FAIL; + } + } + else + { + if ( src.width != dst.width || src.height != dst.height ) + { + result.Release(); + return E_FAIL; + } + } + + if (wicpf) + { + // Case 1: Source format is supported by Windows Imaging Component + hr = _PerformFlipRotateUsingWIC( src, flags, pfGUID, dst ); + } + else + { + // Case 2: Source format is not supported by WIC, so we have to convert, flip/rotate, and convert back + hr = _PerformFlipRotateViaF32( src, flags, dst ); + } + + if ( FAILED(hr) ) + { + result.Release(); + return hr; + } + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexImage.cpp b/DirectXTex/DirectXTexImage.cpp new file mode 100644 index 0000000..0dddd8f --- /dev/null +++ b/DirectXTex/DirectXTexImage.cpp @@ -0,0 +1,638 @@ +//------------------------------------------------------------------------------------- +// DirectXTexImage.cpp +// +// DirectX Texture Library - Image container +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +namespace DirectX +{ + +extern bool _CalculateMipLevels( _In_ size_t width, _In_ size_t height, _Inout_ size_t& mipLevels ); +extern bool _CalculateMipLevels3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t& mipLevels ); + +//------------------------------------------------------------------------------------- +// Determines number of image array entries and pixel size +//------------------------------------------------------------------------------------- +void _DetermineImageArray( const TexMetadata& metadata, DWORD cpFlags, + size_t& nImages, size_t& pixelSize ) +{ + assert( metadata.width > 0 && metadata.height > 0 && metadata.depth > 0 ); + assert( metadata.arraySize > 0 ); + assert( metadata.mipLevels > 0 ); + + size_t _pixelSize = 0; + size_t _nimages = 0; + + switch( metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + case TEX_DIMENSION_TEXTURE2D: + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + size_t w = metadata.width; + size_t h = metadata.height; + + for( size_t level=0; level < metadata.mipLevels; ++level ) + { + size_t rowPitch, slicePitch; + ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags ); + + _pixelSize += slicePitch; + ++_nimages; + + if ( h > 1 ) + h >>= 1; + + if ( w > 1 ) + w >>= 1; + } + } + break; + + case TEX_DIMENSION_TEXTURE3D: + { + size_t w = metadata.width; + size_t h = metadata.height; + size_t d = metadata.depth; + + for( size_t level=0; level < metadata.mipLevels; ++level ) + { + size_t rowPitch, slicePitch; + ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags ); + + for( size_t slice=0; slice < d; ++slice ) + { + _pixelSize += slicePitch; + ++_nimages; + } + + if ( h > 1 ) + h >>= 1; + + if ( w > 1 ) + w >>= 1; + + if ( d > 1 ) + d >>= 1; + } + } + break; + + default: + assert( false ); + break; + } + + nImages = _nimages; + pixelSize = _pixelSize; +} + + +//------------------------------------------------------------------------------------- +// Fills in the image array entries +//------------------------------------------------------------------------------------- +bool _SetupImageArray( uint8_t *pMemory, size_t pixelSize, + const TexMetadata& metadata, DWORD cpFlags, + Image* images, size_t nImages ) +{ + assert( pMemory ); + assert( pixelSize > 0 ); + assert( nImages > 0 ); + + if ( !images ) + return false; + + size_t index = 0; + uint8_t* pixels = pMemory; + const uint8_t* pEndBits = pMemory + pixelSize; + + switch( metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + case TEX_DIMENSION_TEXTURE2D: + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + size_t w = metadata.width; + size_t h = metadata.height; + + for( size_t level=0; level < metadata.mipLevels; ++level ) + { + if ( index >= nImages ) + { + return false; + } + + size_t rowPitch, slicePitch; + ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags ); + + images[index].width = w; + images[index].height = h; + images[index].format = metadata.format; + images[index].rowPitch = rowPitch; + images[index].slicePitch = slicePitch; + images[index].pixels = pixels; + ++index; + + pixels += slicePitch; + if ( pixels > pEndBits ) + { + return false; + } + + if ( h > 1 ) + h >>= 1; + + if ( w > 1 ) + w >>= 1; + } + } + return true; + + case TEX_DIMENSION_TEXTURE3D: + { + size_t w = metadata.width; + size_t h = metadata.height; + size_t d = metadata.depth; + + for( size_t level=0; level < metadata.mipLevels; ++level ) + { + size_t rowPitch, slicePitch; + ComputePitch( metadata.format, w, h, rowPitch, slicePitch, cpFlags ); + + for( size_t slice=0; slice < d; ++slice ) + { + if ( index >= nImages ) + { + return false; + } + + // We use the same memory organization that Direct3D 11 needs for D3D11_SUBRESOURCE_DATA + // with all slices of a given miplevel being continuous in memory + images[index].width = w; + images[index].height = h; + images[index].format = metadata.format; + images[index].rowPitch = rowPitch; + images[index].slicePitch = slicePitch; + images[index].pixels = pixels; + ++index; + + pixels += slicePitch; + if ( pixels > pEndBits ) + { + return false; + } + } + + if ( h > 1 ) + h >>= 1; + + if ( w > 1 ) + w >>= 1; + + if ( d > 1 ) + d >>= 1; + } + } + return true; + + default: + return false; + } +} + + +//===================================================================================== +// ScratchImage - Bitmap image container +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Methods +//------------------------------------------------------------------------------------- +HRESULT ScratchImage::Initialize( const TexMetadata& mdata ) +{ + if ( !IsValid(mdata.format) || IsVideo(mdata.format) ) + return E_INVALIDARG; + + size_t mipLevels = mdata.mipLevels; + + switch( mdata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + if ( !mdata.width || mdata.height != 1 || mdata.depth != 1 || !mdata.arraySize ) + return E_INVALIDARG; + + if ( !_CalculateMipLevels(mdata.width,1,mipLevels) ) + return E_INVALIDARG; + break; + + case TEX_DIMENSION_TEXTURE2D: + if ( !mdata.width || !mdata.height || mdata.depth != 1 || !mdata.arraySize ) + return E_INVALIDARG; + + if ( mdata.miscFlags & TEX_MISC_TEXTURECUBE ) + { + if ( (mdata.arraySize % 6) != 0 ) + return E_INVALIDARG; + } + + if ( !_CalculateMipLevels(mdata.width,mdata.height,mipLevels) ) + return E_INVALIDARG; + break; + + case TEX_DIMENSION_TEXTURE3D: + if ( !mdata.width || !mdata.height || !mdata.depth || mdata.arraySize != 1 ) + return E_INVALIDARG; + + if ( !_CalculateMipLevels3D(mdata.width,mdata.height,mdata.depth,mipLevels) ) + return E_INVALIDARG; + break; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + Release(); + + _metadata.width = mdata.width; + _metadata.height = mdata.height; + _metadata.depth = mdata.depth; + _metadata.arraySize = mdata.arraySize; + _metadata.mipLevels = mipLevels; + _metadata.miscFlags = mdata.miscFlags & TEX_MISC_TEXTURECUBE; + _metadata.format = mdata.format; + _metadata.dimension = mdata.dimension; + + size_t pixelSize, nimages; + _DetermineImageArray( _metadata, CP_FLAGS_NONE, nimages, pixelSize ); + + _image = new Image[ nimages ]; + if ( !_image ) + return E_OUTOFMEMORY; + + _nimages = nimages; + memset( _image, 0, sizeof(Image) * nimages ); + + _memory = reinterpret_cast( _aligned_malloc( pixelSize, 16 ) ); + if ( !_memory ) + { + Release(); + return E_OUTOFMEMORY; + } + _size = pixelSize; + if ( !_SetupImageArray( _memory, pixelSize, _metadata, CP_FLAGS_NONE, _image, nimages ) ) + { + Release(); + return E_FAIL; + } + + return S_OK; +} + +HRESULT ScratchImage::Initialize1D( DXGI_FORMAT fmt, size_t length, size_t arraySize, size_t mipLevels ) +{ + if ( !IsValid(fmt) || IsVideo(fmt) || !length || !arraySize ) + return E_INVALIDARG; + + // 1D is a special case of the 2D case + HRESULT hr = Initialize2D( fmt, length, 1, arraySize, mipLevels ); + if ( FAILED(hr) ) + return hr; + + _metadata.dimension = TEX_DIMENSION_TEXTURE1D; + + return S_OK; +} + +HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height, size_t arraySize, size_t mipLevels ) +{ + if ( !IsValid(fmt) || IsVideo(fmt) || !width || !height || !arraySize ) + return E_INVALIDARG; + + if ( !_CalculateMipLevels(width,height,mipLevels) ) + return E_INVALIDARG; + + Release(); + + _metadata.width = width; + _metadata.height = height; + _metadata.depth = 1; + _metadata.arraySize = arraySize; + _metadata.mipLevels = mipLevels; + _metadata.miscFlags = 0; + _metadata.format = fmt; + _metadata.dimension = TEX_DIMENSION_TEXTURE2D; + + size_t pixelSize, nimages; + _DetermineImageArray( _metadata, CP_FLAGS_NONE, nimages, pixelSize ); + + _image = new Image[ nimages ]; + if ( !_image ) + return E_OUTOFMEMORY; + + _nimages = nimages; + memset( _image, 0, sizeof(Image) * nimages ); + + _memory = reinterpret_cast( _aligned_malloc( pixelSize, 16 ) ); + if ( !_memory ) + { + Release(); + return E_OUTOFMEMORY; + } + _size = pixelSize; + if ( !_SetupImageArray( _memory, pixelSize, _metadata, CP_FLAGS_NONE, _image, nimages ) ) + { + Release(); + return E_FAIL; + } + + return S_OK; +} + +HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height, size_t depth, size_t mipLevels ) +{ + if ( !IsValid(fmt) || IsVideo(fmt) || !width || !height || !depth ) + return E_INVALIDARG; + + if ( !_CalculateMipLevels3D(width,height,depth,mipLevels) ) + return E_INVALIDARG; + + Release(); + + _metadata.width = width; + _metadata.height = height; + _metadata.depth = depth; + _metadata.arraySize = 1; // Direct3D 10.x/11 does not support arrays of 3D textures + _metadata.mipLevels = mipLevels; + _metadata.miscFlags = 0; + _metadata.format = fmt; + _metadata.dimension = TEX_DIMENSION_TEXTURE3D; + + size_t pixelSize, nimages; + _DetermineImageArray( _metadata, CP_FLAGS_NONE, nimages, pixelSize ); + + _image = new Image[ nimages ]; + if ( !_image ) + { + Release(); + return E_OUTOFMEMORY; + } + _nimages = nimages; + memset( _image, 0, sizeof(Image) * nimages ); + + _memory = reinterpret_cast( _aligned_malloc( pixelSize, 16 ) ); + if ( !_memory ) + { + Release(); + return E_OUTOFMEMORY; + } + _size = pixelSize; + + if ( !_SetupImageArray( _memory, pixelSize, _metadata, CP_FLAGS_NONE, _image, nimages ) ) + { + Release(); + return E_FAIL; + } + + return S_OK; +} + +HRESULT ScratchImage::InitializeCube( DXGI_FORMAT fmt, size_t width, size_t height, size_t nCubes, size_t mipLevels ) +{ + if ( !IsValid(fmt) || IsVideo(fmt) || !width || !height || !nCubes ) + return E_INVALIDARG; + + // A DirectX11 cubemap is just a 2D texture array that is a multiple of 6 for each cube + HRESULT hr = Initialize2D( fmt, width, height, nCubes * 6, mipLevels ); + if ( FAILED(hr) ) + return hr; + + _metadata.miscFlags |= TEX_MISC_TEXTURECUBE; + + return S_OK; +} + +HRESULT ScratchImage::InitializeFromImage( const Image& srcImage, bool allow1D ) +{ + HRESULT hr = ( srcImage.height > 1 || !allow1D ) + ? Initialize2D( srcImage.format, srcImage.width, srcImage.height, 1, 1 ) + : Initialize1D( srcImage.format, srcImage.width, 1, 1 ); + + if ( FAILED(hr) ) + return hr; + + const uint8_t* sptr = reinterpret_cast( srcImage.pixels ); + if ( !sptr ) + return E_POINTER; + + uint8_t* dptr = reinterpret_cast( _image[0].pixels ); + if ( !dptr ) + return E_POINTER; + + for( size_t y = 0; y < srcImage.height; ++y ) + { + _CopyScanline( dptr, _image[0].rowPitch, sptr, srcImage.rowPitch, srcImage.format, TEXP_SCANLINE_NONE ); + sptr += srcImage.rowPitch; + dptr += _image[0].rowPitch; + } + + return S_OK; +} + +HRESULT ScratchImage::InitializeArrayFromImages( const Image* images, size_t nImages, bool allow1D ) +{ + if ( !images || !nImages ) + return E_INVALIDARG; + + DXGI_FORMAT format = images[0].format; + size_t width = images[0].width; + size_t height = images[0].height; + + for( size_t index=0; index < nImages; ++index ) + { + if ( !images[index].pixels ) + return E_POINTER; + + if ( images[index].format != format || images[index].width != width || images[index].height != height ) + { + // All images must be the same format, width, and height + return E_FAIL; + } + } + + HRESULT hr = ( height > 1 || !allow1D ) + ? Initialize2D( format, width, height, nImages, 1 ) + : Initialize1D( format, width, nImages, 1 ); + + if ( FAILED(hr) ) + return hr; + + for( size_t index=0; index < nImages; ++index ) + { + const uint8_t* sptr = reinterpret_cast( images[index].pixels ); + if ( !sptr ) + return E_POINTER; + + assert( index < _nimages ); + uint8_t* dptr = reinterpret_cast( _image[index].pixels ); + if ( !dptr ) + return E_POINTER; + + for( size_t y = 0; y < height; ++y ) + { + _CopyScanline( dptr, _image[index].rowPitch, sptr, images[index].rowPitch, format, TEXP_SCANLINE_NONE ); + sptr += images[index].rowPitch; + dptr += _image[index].rowPitch; + } + } + + return S_OK; +} + +HRESULT ScratchImage::InitializeCubeFromImages( const Image* images, size_t nImages ) +{ + if ( !images || !nImages ) + return E_INVALIDARG; + + // A DirectX11 cubemap is just a 2D texture array that is a multiple of 6 for each cube + if ( ( nImages % 6 ) != 0 ) + return E_INVALIDARG; + + HRESULT hr = InitializeArrayFromImages( images, nImages, false ); + if ( FAILED(hr) ) + return hr; + + _metadata.miscFlags |= TEX_MISC_TEXTURECUBE; + + return S_OK; +} + +HRESULT ScratchImage::Initialize3DFromImages( const Image* images, size_t depth ) +{ + if ( !images || !depth ) + return E_INVALIDARG; + + DXGI_FORMAT format = images[0].format; + size_t width = images[0].width; + size_t height = images[0].height; + + for( size_t slice=0; slice < depth; ++slice ) + { + if ( !images[slice].pixels ) + return E_POINTER; + + if ( images[slice].format != format || images[slice].width != width || images[slice].height != height ) + { + // All images must be the same format, width, and height + return E_FAIL; + } + } + + HRESULT hr = Initialize3D( format, width, height, depth, 1 ); + if ( FAILED(hr) ) + return hr; + + for( size_t slice=0; slice < depth; ++slice ) + { + const uint8_t* sptr = reinterpret_cast( images[slice].pixels ); + if ( !sptr ) + return E_POINTER; + + assert( slice < _nimages ); + uint8_t* dptr = reinterpret_cast( _image[slice].pixels ); + if ( !dptr ) + return E_POINTER; + + for( size_t y = 0; y < height; ++y ) + { + _CopyScanline( dptr, _image[slice].rowPitch, sptr, images[slice].rowPitch, format, TEXP_SCANLINE_NONE ); + sptr += images[slice].rowPitch; + dptr += _image[slice].rowPitch; + } + } + + return S_OK; +} + +void ScratchImage::Release() +{ + _nimages = 0; + _size = 0; + + if ( _image ) + { + delete [] _image; + _image = 0; + } + + if ( _memory ) + { + _aligned_free( _memory ); + _memory = 0; + } + + memset(&_metadata, 0, sizeof(_metadata)); +} + +const Image* ScratchImage::GetImage(size_t mip, size_t item, size_t slice) const +{ + if ( mip >= _metadata.mipLevels ) + return nullptr; + + size_t index = 0; + + switch( _metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + case TEX_DIMENSION_TEXTURE2D: + if ( slice > 0 ) + return nullptr; + + if ( item >= _metadata.arraySize ) + return nullptr; + + index = item*( _metadata.mipLevels ) + mip; + break; + + case TEX_DIMENSION_TEXTURE3D: + if ( item > 0 ) + { + // No support for arrays of volumes + return nullptr; + } + else + { + size_t d = _metadata.depth; + + for( size_t level = 0; level < mip; ++level ) + { + index += d; + if ( d > 1 ) + d >>= 1; + } + + if ( slice >= d ) + return nullptr; + + index += slice; + } + break; + + default: + return nullptr; + } + + return &_image[index]; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexMipmaps.cpp b/DirectXTex/DirectXTexMipmaps.cpp new file mode 100644 index 0000000..dadddf0 --- /dev/null +++ b/DirectXTex/DirectXTexMipmaps.cpp @@ -0,0 +1,1157 @@ +//------------------------------------------------------------------------------------- +// DirectXTexMipMaps.cpp +// +// DirectX Texture Library - Mip-map generation +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +namespace DirectX +{ + +static const XMVECTORF32 s_boxScale = { 0.25f, 0.25f, 0.25f, 0.25f }; +static const XMVECTORF32 s_boxScale3D = { 0.125f, 0.125f, 0.125f, 0.125f }; + +//------------------------------------------------------------------------------------- +// Mipmap helper functions +//------------------------------------------------------------------------------------- +inline static bool ispow2( _In_ size_t x ) +{ + return ((x != 0) && !(x & (x - 1))); +} + +static size_t _CountMips( _In_ size_t width, _In_ size_t height) +{ + size_t mipLevels = 1; + + while ( height > 1 || width > 1 ) + { + if ( height > 1 ) + height >>= 1; + + if ( width > 1 ) + width >>= 1; + + ++mipLevels; + } + + return mipLevels; +} + +bool _CalculateMipLevels( _In_ size_t width, _In_ size_t height, _Inout_ size_t& mipLevels ) +{ + if ( mipLevels > 1 ) + { + size_t maxMips = _CountMips(width,height); + if ( mipLevels > maxMips ) + return false; + } + else if ( mipLevels == 0 ) + { + mipLevels = _CountMips(width,height); + } + else + { + mipLevels = 1; + } + return true; +} + +static size_t _CountMips3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth) +{ + size_t mipLevels = 1; + + while ( height > 1 || width > 1 || depth > 1 ) + { + if ( height > 1 ) + height >>= 1; + + if ( width > 1 ) + width >>= 1; + + if ( depth > 1 ) + depth >>= 1; + + ++mipLevels; + } + + return mipLevels; +} + +bool _CalculateMipLevels3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t& mipLevels ) +{ + if ( mipLevels > 1 ) + { + if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) ) + return false; + + size_t maxMips = _CountMips3D(width,height,depth); + if ( mipLevels > maxMips ) + return false; + } + else if ( mipLevels == 0 && ispow2(width) && ispow2(height) && ispow2(depth) ) + { + mipLevels = _CountMips3D(width,height,depth); + } + else + { + mipLevels = 1; + } + return true; +} + +static HRESULT _EnsureWicBitmapPixelFormat( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* src, _In_ DWORD filter, + _In_ const WICPixelFormatGUID& desiredPixelFormat, + _Deref_out_ IWICBitmap** dest ) +{ + if ( !pWIC || !src || !dest ) + return E_POINTER; + + *dest = nullptr; + + WICPixelFormatGUID actualPixelFormat; + HRESULT hr = src->GetPixelFormat( &actualPixelFormat ); + + if ( SUCCEEDED(hr) ) + { + if ( memcmp( &actualPixelFormat, &desiredPixelFormat, sizeof(WICPixelFormatGUID) ) == 0 ) + { + src->AddRef(); + *dest = src; + } + else + { + ScopedObject converter; + hr = pWIC->CreateFormatConverter( &converter ); + if ( SUCCEEDED(hr) ) + { + hr = converter->Initialize( src, desiredPixelFormat, _GetWICDither(filter), 0, 0, WICBitmapPaletteTypeCustom ); + } + + if ( SUCCEEDED(hr) ) + { + hr = pWIC->CreateBitmapFromSource( converter.Get(), WICBitmapCacheOnDemand, dest ); + } + } + } + + return hr; +} + +HRESULT _ResizeSeparateColorAndAlpha( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* original, + _In_ size_t newWidth, _In_ size_t newHeight, _In_ DWORD filter, _Inout_ const Image* img ) +{ + if ( !pWIC || !original || !img ) + return E_POINTER; + + const WICBitmapInterpolationMode interpolationMode = _GetWICInterp(filter); + + WICPixelFormatGUID desiredPixelFormat = GUID_WICPixelFormatUndefined; + HRESULT hr = original->GetPixelFormat( &desiredPixelFormat ); + + size_t colorBytesInPixel = 0; + size_t colorBytesPerPixel = 0; + size_t colorWithAlphaBytesPerPixel = 0; + WICPixelFormatGUID colorPixelFormat = GUID_WICPixelFormatUndefined; + WICPixelFormatGUID colorWithAlphaPixelFormat = GUID_WICPixelFormatUndefined; + + if ( SUCCEEDED(hr) ) + { + ScopedObject componentInfo; + hr = pWIC->CreateComponentInfo( desiredPixelFormat, &componentInfo ); + + ScopedObject pixelFormatInfo; + if ( SUCCEEDED(hr) ) + { + hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo), (void**)&pixelFormatInfo ); + } + + UINT bitsPerPixel = 0; + if ( SUCCEEDED(hr) ) + { + hr = pixelFormatInfo->GetBitsPerPixel( &bitsPerPixel ); + } + + if ( SUCCEEDED(hr) ) + { + if ( bitsPerPixel <= 32 ) + { + colorBytesInPixel = colorBytesPerPixel = 3; + colorPixelFormat = GUID_WICPixelFormat24bppBGR; + + colorWithAlphaBytesPerPixel = 4; + colorWithAlphaPixelFormat = GUID_WICPixelFormat32bppBGRA; + } + else + { +#if(_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + colorBytesInPixel = colorBytesPerPixel = 12; + colorPixelFormat = GUID_WICPixelFormat96bppRGBFloat; +#else + colorBytesInPixel = 12; + colorBytesPerPixel = 16; + colorPixelFormat = GUID_WICPixelFormat128bppRGBFloat; +#endif + colorWithAlphaBytesPerPixel = 16; + colorWithAlphaPixelFormat = GUID_WICPixelFormat128bppRGBAFloat; + } + } + } + + // Resize color only image (no alpha channel) + ScopedObject resizedColor; + if ( SUCCEEDED(hr) ) + { + ScopedObject colorScaler; + + hr = pWIC->CreateBitmapScaler(&colorScaler); + if ( SUCCEEDED(hr) ) + { + ScopedObject converted; + + hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorPixelFormat, &converted ); + if ( SUCCEEDED(hr) ) + { + hr = colorScaler->Initialize( converted.Get(), static_cast(newWidth), static_cast(newHeight), interpolationMode ); + } + } + + if ( SUCCEEDED(hr) ) + { + ScopedObject resized; + + hr = pWIC->CreateBitmapFromSource( colorScaler.Get(), WICBitmapCacheOnDemand, &resized ); + if ( SUCCEEDED(hr) ) + { + hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorPixelFormat, &resizedColor ); + } + } + } + + // Resize color+alpha image + ScopedObject resizedColorWithAlpha; + if ( SUCCEEDED(hr) ) + { + ScopedObject colorWithAlphaScaler; + + hr = pWIC->CreateBitmapScaler( &colorWithAlphaScaler ); + if ( SUCCEEDED(hr) ) + { + ScopedObject converted; + + hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorWithAlphaPixelFormat, &converted ); + if ( SUCCEEDED(hr) ) + { + hr = colorWithAlphaScaler->Initialize( converted.Get(), static_cast(newWidth), static_cast(newHeight), interpolationMode ); + } + } + + if ( SUCCEEDED(hr) ) + { + ScopedObject resized; + + hr = pWIC->CreateBitmapFromSource( colorWithAlphaScaler.Get(), WICBitmapCacheOnDemand, &resized ); + if ( SUCCEEDED(hr) ) + { + hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorWithAlphaPixelFormat, &resizedColorWithAlpha ); + } + } + } + + // Merge pixels (copying color channels from color only image to color+alpha image) + if ( SUCCEEDED(hr) ) + { + ScopedObject colorLock; + ScopedObject colorWithAlphaLock; + + hr = resizedColor->Lock( nullptr, WICBitmapLockRead, &colorLock ); + if ( SUCCEEDED(hr) ) + { + hr = resizedColorWithAlpha->Lock( nullptr, WICBitmapLockWrite, &colorWithAlphaLock ); + } + + if ( SUCCEEDED(hr) ) + { + WICInProcPointer colorWithAlphaData = nullptr; + UINT colorWithAlphaSizeInBytes = 0; + UINT colorWithAlphaStride = 0; + + hr = colorWithAlphaLock->GetDataPointer( &colorWithAlphaSizeInBytes, &colorWithAlphaData ); + if ( SUCCEEDED(hr) ) + { + if ( !colorWithAlphaData ) + { + hr = E_POINTER; + } + else + { + hr = colorWithAlphaLock->GetStride( &colorWithAlphaStride ); + } + } + + WICInProcPointer colorData = nullptr; + UINT colorSizeInBytes = 0; + UINT colorStride = 0; + if ( SUCCEEDED(hr) ) + { + hr = colorLock->GetDataPointer( &colorSizeInBytes, &colorData ); + if ( SUCCEEDED(hr) ) + { + if ( !colorData ) + { + hr = E_POINTER; + } + else + { + hr = colorLock->GetStride( &colorStride ); + } + } + } + + for ( size_t j = 0; SUCCEEDED(hr) && j < newHeight; j++ ) + { + for ( size_t i = 0; SUCCEEDED(hr) && i < newWidth; i++ ) + { + size_t colorWithAlphaIndex = (j * colorWithAlphaStride) + (i * colorWithAlphaBytesPerPixel); + size_t colorIndex = (j * colorStride) + (i * colorBytesPerPixel); + + if ( ((colorWithAlphaIndex + colorBytesInPixel) > colorWithAlphaSizeInBytes) + || ( (colorIndex + colorBytesPerPixel) > colorSizeInBytes) ) + { + hr = E_INVALIDARG; + } + else + { + memcpy_s( colorWithAlphaData + colorWithAlphaIndex, colorWithAlphaBytesPerPixel, colorData + colorIndex, colorBytesInPixel ); + } + } + } + } + } + + if ( SUCCEEDED(hr) ) + { + ScopedObject wicBitmap; + hr = _EnsureWicBitmapPixelFormat( pWIC, resizedColorWithAlpha.Get(), filter, desiredPixelFormat, &wicBitmap ); + if ( SUCCEEDED(hr) ) + { + hr = wicBitmap->CopyPixels( nullptr, static_cast(img->rowPitch), static_cast(img->slicePitch), img->pixels ); + } + } + + return hr; +} + + +//------------------------------------------------------------------------------------- +// Generate a (2D) mip-map chain from a base image using WIC's image scaler +//------------------------------------------------------------------------------------- +static HRESULT _GenerateMipMapsUsingWIC( _In_ const Image& baseImage, _In_ DWORD filter, _In_ size_t levels, + _In_ const WICPixelFormatGUID& pfGUID, _In_ const ScratchImage& mipChain, _In_ size_t item ) +{ + assert( levels > 1 ); + + if ( !baseImage.pixels || !mipChain.GetPixels() ) + return E_POINTER; + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + size_t width = baseImage.width; + size_t height = baseImage.height; + + ScopedObject source; + HRESULT hr = pWIC->CreateBitmapFromMemory( static_cast( width ), static_cast( height ), pfGUID, + static_cast( baseImage.rowPitch ), static_cast( baseImage.slicePitch ), + baseImage.pixels, &source ); + if ( FAILED(hr) ) + return hr; + + // Copy base image to top miplevel + const Image *img0 = mipChain.GetImage( 0, item, 0 ); + if ( !img0 ) + return E_POINTER; + + uint8_t* pDest = img0->pixels; + if ( !pDest ) + return E_POINTER; + + const uint8_t *pSrc = baseImage.pixels; + for( size_t h=0; h < height; ++h ) + { + size_t msize = std::min( img0->rowPitch, baseImage.rowPitch ); + memcpy_s( pDest, img0->rowPitch, pSrc, msize ); + pSrc += baseImage.rowPitch; + pDest += img0->rowPitch; + } + + ScopedObject componentInfo; + hr = pWIC->CreateComponentInfo( pfGUID, &componentInfo ); + if ( FAILED(hr) ) + return hr; + + ScopedObject pixelFormatInfo; + hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo2), (void**)&pixelFormatInfo ); + if ( FAILED(hr) ) + return hr; + + BOOL supportsTransparency = FALSE; + hr = pixelFormatInfo->SupportsTransparency( &supportsTransparency ); + if ( FAILED(hr) ) + return hr; + + // Resize base image to each target mip level + for( size_t level = 1; level < levels; ++level ) + { + const Image *img = mipChain.GetImage( level, item, 0 ); + if ( !img ) + return E_POINTER; + + if ( height > 1 ) + height >>= 1; + + if ( width > 1 ) + width >>= 1; + + assert( img->width == width && img->height == height && img->format == baseImage.format ); + + if ( (filter & TEX_FILTER_SEPARATE_ALPHA) && supportsTransparency ) + { + hr = _ResizeSeparateColorAndAlpha( pWIC, source.Get(), width, height, filter, img ); + if ( FAILED(hr) ) + return hr; + } + else + { + ScopedObject scaler; + hr = pWIC->CreateBitmapScaler( &scaler ); + if ( FAILED(hr) ) + return hr; + + hr = scaler->Initialize( source.Get(), static_cast( width ), static_cast( height ), _GetWICInterp( filter ) ); + if ( FAILED(hr) ) + return hr; + + WICPixelFormatGUID pfScaler; + hr = scaler->GetPixelFormat( &pfScaler ); + if ( FAILED(hr) ) + return hr; + + if ( memcmp( &pfScaler, &pfGUID, sizeof(WICPixelFormatGUID) ) == 0 ) + { + hr = scaler->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + else + { + // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we + // convert it back + ScopedObject FC; + hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( scaler.Get(), pfGUID, _GetWICDither( filter ), 0, 0, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + } + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Generate volume mip-map helpers +//------------------------------------------------------------------------------------- +static HRESULT _Setup3DMips( _In_count_(depth) const Image* baseImages, _In_ size_t depth, size_t levels, + _Out_ ScratchImage& mipChain ) +{ + if ( !baseImages || !depth ) + return E_INVALIDARG; + + assert( levels > 1 ); + + size_t width = baseImages[0].width; + size_t height = baseImages[0].height; + + HRESULT hr = mipChain.Initialize3D( baseImages[0].format, width, height, depth, levels ); + if ( FAILED(hr) ) + return hr; + + // Copy base images to top slice + for( size_t slice=0; slice < depth; ++slice ) + { + const Image& src = baseImages[slice]; + + const Image *dest = mipChain.GetImage( 0, 0, slice ); + if ( !dest ) + { + mipChain.Release(); + return E_POINTER; + } + + assert( src.format == dest->format ); + + uint8_t* pDest = dest->pixels; + if ( !pDest ) + { + mipChain.Release(); + return E_POINTER; + } + + const uint8_t *pSrc = src.pixels; + size_t rowPitch = src.rowPitch; + for( size_t h=0; h < height; ++h ) + { + size_t msize = std::min( dest->rowPitch, rowPitch ); + memcpy_s( pDest, dest->rowPitch, pSrc, msize ); + pSrc += rowPitch; + pDest += dest->rowPitch; + } + } + + return S_OK; +} + +static HRESULT _Generate3DMipsPointFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain ) +{ + if ( !depth || !mipChain.GetImages() ) + return E_INVALIDARG; + + // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips) + + assert( levels > 1 ); + + size_t width = mipChain.GetMetadata().width; + size_t height = mipChain.GetMetadata().height; + + assert( ispow2(width) && ispow2(height) && ispow2(depth) ); + + // Allocate temporary space (2 scanlines) + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*width*2), 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + XMVECTOR* target = scanline.get(); + + XMVECTOR* row = target + width; + + // Resize base image to each target mip level + for( size_t level=1; level < levels; ++level ) + { + if ( depth > 1 ) + { + // 3D point filter + for( size_t slice=0; slice < depth; slice += 2 ) + { + const Image* src = mipChain.GetImage( level-1, 0, slice ); + const Image* dest = mipChain.GetImage( level, 0, slice >> 1 ); + + if ( !src || !dest ) + return E_POINTER; + + const uint8_t* pSrc = src->pixels; + uint8_t* pDest = dest->pixels; + + size_t rowPitch = src->rowPitch; + + size_t nheight = height >> 1; + + for( size_t y = 0; y < nheight; ++y ) + { + if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) ) + return E_FAIL; + pSrc += rowPitch*2; + + size_t nwidth = width >> 1; + + for( size_t x = 0; x < nwidth; ++x ) + { + target[ x ] = row[ x*2 ]; + } + + if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) ) + return E_FAIL; + pDest += dest->rowPitch; + } + } + } + else + { + // 2D point filter + const Image* src = mipChain.GetImage( level-1, 0, 0 ); + const Image* dest = mipChain.GetImage( level, 0, 0 ); + + if ( !src || !dest ) + return E_POINTER; + + const uint8_t* pSrc = src->pixels; + uint8_t* pDest = dest->pixels; + + size_t rowPitch = src->rowPitch; + + size_t nheight = height >> 1; + + for( size_t y = 0; y < nheight; ++y ) + { + if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) ) + return E_FAIL; + pSrc += rowPitch*2; + + size_t nwidth = width >> 1; + + for( size_t x = 0; x < nwidth; ++x ) + { + target[ x ] = row[ x*2 ]; + } + + if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) ) + return E_FAIL; + pDest += dest->rowPitch; + } + } + + if ( height > 1 ) + height >>= 1; + + if ( width > 1 ) + width >>= 1; + + if ( depth > 1 ) + depth >>= 1; + } + + assert( height == 1 && width == 1 && depth == 1 ); + + return S_OK; +} + +static HRESULT _Generate3DMipsBoxFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain ) +{ + if ( !depth || !mipChain.GetImages() ) + return E_INVALIDARG; + + // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips) + + assert( levels > 1 ); + + size_t width = mipChain.GetMetadata().width; + size_t height = mipChain.GetMetadata().height; + + assert( ispow2(width) && ispow2(height) && ispow2(depth) ); + + // Allocate temporary space (5 scanlines) + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*width*5), 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + XMVECTOR* target = scanline.get(); + + XMVECTOR* urow0 = target + width; + XMVECTOR* urow1 = target + width*2; + XMVECTOR* vrow0 = target + width*3; + XMVECTOR* vrow1 = target + width*4; + + const XMVECTOR* urow2 = urow0 + 1; + const XMVECTOR* urow3 = urow1 + 1; + const XMVECTOR* vrow2 = vrow0 + 1; + const XMVECTOR* vrow3 = vrow1 + 1; + + // Resize base image to each target mip level + for( size_t level=1; level < levels; ++level ) + { + if ( height == 1) + { + urow0 = vrow0; + urow1 = vrow1; + } + + if ( width == 1 ) + { + urow2 = urow0; + urow3 = urow1; + vrow2 = vrow0; + vrow3 = vrow1; + } + + if ( depth > 1 ) + { + // 3D box filter + for( size_t slice=0; slice < depth; slice += 2 ) + { + const Image* srca = mipChain.GetImage( level-1, 0, slice ); + const Image* srcb = mipChain.GetImage( level-1, 0, slice+1 ); + const Image* dest = mipChain.GetImage( level, 0, slice >> 1 ); + + if ( !srca || !srcb || !dest ) + return E_POINTER; + + const uint8_t* pSrc1 = srca->pixels; + const uint8_t* pSrc2 = srcb->pixels; + uint8_t* pDest = dest->pixels; + + size_t aRowPitch = srca->rowPitch; + size_t bRowPitch = srcb->rowPitch; + + size_t nheight = height >> 1; + + for( size_t y = 0; y < nheight; ++y ) + { + if ( !_LoadScanline( urow0, width, pSrc1, aRowPitch, srca->format ) ) + return E_FAIL; + pSrc1 += aRowPitch; + + if ( urow0 != urow1 ) + { + if ( !_LoadScanline( urow1, width, pSrc1, aRowPitch, srca->format ) ) + return E_FAIL; + pSrc1 += aRowPitch; + } + + if ( urow0 != vrow0 ) + { + if ( !_LoadScanline( vrow0, width, pSrc2, bRowPitch, srcb->format ) ) + return E_FAIL; + pSrc2 += bRowPitch; + } + + if ( urow0 != vrow1 && vrow0 != vrow1 ) + { + if ( !_LoadScanline( vrow1, width, pSrc2, bRowPitch, srcb->format ) ) + return E_FAIL; + pSrc2 += bRowPitch; + } + + size_t nwidth = width >> 1; + + for( size_t x = 0; x < nwidth; ++x ) + { + size_t x2 = x*2; + + // Box filter: Average 2x2x2 pixels + XMVECTOR v = XMVectorAdd( urow0[ x2 ], urow1[ x2 ] ); + v = XMVectorAdd( v, urow2[ x2 ] ); + v = XMVectorAdd( v, urow3[ x2 ] ); + v = XMVectorAdd( v, vrow0[ x2 ] ); + v = XMVectorAdd( v, vrow1[ x2 ] ); + v = XMVectorAdd( v, vrow2[ x2 ] ); + v = XMVectorAdd( v, vrow3[ x2 ] ); + + target[ x ] = XMVectorMultiply( v, s_boxScale3D ); + } + + if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) ) + return E_FAIL; + pDest += dest->rowPitch; + } + } + } + else + { + // 2D box filter + const Image* src = mipChain.GetImage( level-1, 0, 0 ); + const Image* dest = mipChain.GetImage( level, 0, 0 ); + + if ( !src || !dest ) + return E_POINTER; + + const uint8_t* pSrc = src->pixels; + uint8_t* pDest = dest->pixels; + + size_t rowPitch = src->rowPitch; + + size_t nheight = height >> 1; + + for( size_t y = 0; y < nheight; ++y ) + { + if ( !_LoadScanline( urow0, width, pSrc, rowPitch, src->format ) ) + return E_FAIL; + pSrc += rowPitch; + + if ( urow0 != urow1 ) + { + if ( !_LoadScanline( urow1, width, pSrc, rowPitch, src->format ) ) + return E_FAIL; + pSrc += rowPitch; + } + + size_t nwidth = width >> 1; + + for( size_t x = 0; x < nwidth; ++x ) + { + size_t x2 = x*2; + + // Box filter: Average 2x2 pixels + XMVECTOR v = XMVectorAdd( urow0[ x2 ], urow1[ x2 ] ); + v = XMVectorAdd( v, urow2[ x2 ] ); + v = XMVectorAdd( v, urow3[ x2 ] ); + + target[ x ] = XMVectorMultiply( v, s_boxScale ); + } + + if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) ) + return E_FAIL; + pDest += dest->rowPitch; + } + } + + if ( height > 1 ) + height >>= 1; + + if ( width > 1 ) + width >>= 1; + + if ( depth > 1 ) + depth >>= 1; + } + + assert( height == 1 && width == 1 && depth == 1 ); + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Generate mipmap chain +//------------------------------------------------------------------------------------- +HRESULT GenerateMipMaps( const Image& baseImage, DWORD filter, size_t levels, ScratchImage& mipChain, bool allow1D ) +{ + if ( !IsValid( baseImage.format ) ) + return E_INVALIDARG; + + if ( !baseImage.pixels ) + return E_POINTER; + + if ( !_CalculateMipLevels(baseImage.width, baseImage.height, levels) ) + return E_INVALIDARG; + + if ( IsCompressed( baseImage.format ) || IsVideo( baseImage.format ) ) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" ); + switch(filter & TEX_FILTER_MASK) + { + case 0: + case TEX_FILTER_POINT: + case TEX_FILTER_FANT: // Equivalent to Box filter + case TEX_FILTER_LINEAR: + case TEX_FILTER_CUBIC: + { + WICPixelFormatGUID pfGUID; + if ( _DXGIToWIC( baseImage.format, pfGUID ) ) + { + // Case 1: Base image format is supported by Windows Imaging Component + HRESULT hr = (baseImage.height > 1 || !allow1D) + ? mipChain.Initialize2D( baseImage.format, baseImage.width, baseImage.height, 1, levels ) + : mipChain.Initialize1D( baseImage.format, baseImage.width, 1, levels ); + if ( FAILED(hr) ) + return hr; + + return _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, 0 ); + } + else + { + // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back + assert( baseImage.format != DXGI_FORMAT_R32G32B32A32_FLOAT ); + ScratchImage temp; + HRESULT hr = _ConvertToR32G32B32A32( baseImage, temp ); + if ( FAILED(hr) ) + return hr; + + const Image *timg = temp.GetImage( 0, 0, 0 ); + if ( !timg ) + return E_POINTER; + + ScratchImage tMipChain; + hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, 0 ); + if ( FAILED(hr) ) + return hr; + + temp.Release(); + + return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), baseImage.format, mipChain ); + } + } + break; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } +} + +HRESULT GenerateMipMaps( const Image* srcImages, size_t nimages, const TexMetadata& metadata, + DWORD filter, size_t levels, ScratchImage& mipChain ) +{ + if ( !srcImages || !nimages || !IsValid(metadata.format) ) + return E_INVALIDARG; + + if ( metadata.dimension == TEX_DIMENSION_TEXTURE3D + || IsCompressed( metadata.format ) || IsVideo( metadata.format ) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + if ( !_CalculateMipLevels(metadata.width, metadata.height, levels) ) + return E_INVALIDARG; + + static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" ); + switch(filter & TEX_FILTER_MASK) + { + case 0: + case TEX_FILTER_POINT: + case TEX_FILTER_FANT: // Equivalent to Box filter + case TEX_FILTER_LINEAR: + case TEX_FILTER_CUBIC: + { + WICPixelFormatGUID pfGUID; + if ( _DXGIToWIC( metadata.format, pfGUID ) ) + { + // Case 1: Base image format is supported by Windows Imaging Component + TexMetadata mdata2 = metadata; + mdata2.mipLevels = levels; + HRESULT hr = mipChain.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + size_t index = metadata.ComputeIndex( 0, item, 0 ); + if ( index >= nimages ) + { + mipChain.Release(); + return E_FAIL; + } + + const Image& baseImage = srcImages[ index ]; + + hr = _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, item ); + if ( FAILED(hr) ) + { + mipChain.Release(); + return hr; + } + } + + return S_OK; + } + else + { + // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back + assert( metadata.format != DXGI_FORMAT_R32G32B32A32_FLOAT ); + + TexMetadata mdata2 = metadata; + mdata2.mipLevels = levels; + mdata2.format = DXGI_FORMAT_R32G32B32A32_FLOAT; + ScratchImage tMipChain; + HRESULT hr = tMipChain.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + size_t index = metadata.ComputeIndex( 0, item, 0 ); + if ( index >= nimages ) + return E_FAIL; + + const Image& baseImage = srcImages[ index ]; + + ScratchImage temp; + hr = _ConvertToR32G32B32A32( baseImage, temp ); + if ( FAILED(hr) ) + return hr; + + const Image *timg = temp.GetImage( 0, 0, 0 ); + if ( !timg ) + return E_POINTER; + + hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, item ); + if ( FAILED(hr) ) + return hr; + } + + return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), metadata.format, mipChain ); + } + } + break; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );; + } +} + + +//------------------------------------------------------------------------------------- +// Generate mipmap chain for volume texture +//------------------------------------------------------------------------------------- +HRESULT GenerateMipMaps3D( const Image* baseImages, size_t depth, DWORD filter, size_t levels, ScratchImage& mipChain ) +{ + if ( !baseImages || !depth ) + return E_INVALIDARG; + + DXGI_FORMAT format = baseImages[0].format; + size_t width = baseImages[0].width; + size_t height = baseImages[0].height; + + if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) ) + return E_INVALIDARG; + + if ( !_CalculateMipLevels3D(width, height, depth, levels) ) + return E_INVALIDARG; + + for( size_t slice=0; slice < depth; ++slice ) + { + if ( !baseImages[slice].pixels ) + return E_POINTER; + + if ( baseImages[slice].format != format || baseImages[slice].width != width || baseImages[slice].height != height ) + { + // All base images must be the same format, width, and height + return E_FAIL; + } + } + + if ( IsCompressed( format ) ) + { + // We don't support generating mipmaps from compressed images, as those should be generated before compression + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + HRESULT hr; + + static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" ); + switch( filter & TEX_FILTER_MASK ) + { + case 0: + case TEX_FILTER_FANT: + hr = _Setup3DMips( baseImages, depth, levels, mipChain ); + if ( FAILED(hr) ) + return hr; + + // For decimation, Fant is equivalent to a Box filter + hr = _Generate3DMipsBoxFilter( depth, levels, mipChain ); + if ( FAILED(hr) ) + mipChain.Release(); + return hr; + + case WIC_FLAGS_FILTER_POINT: + hr = _Setup3DMips( baseImages, depth, levels, mipChain ); + if ( FAILED(hr) ) + return hr; + + hr = _Generate3DMipsPointFilter( depth, levels, mipChain ); + if ( FAILED(hr) ) + mipChain.Release(); + return hr; + + case WIC_FLAGS_FILTER_LINEAR: + // Need to implement a 3D bi-linear filter (2x2x2) + return E_NOTIMPL; + + case WIC_FLAGS_FILTER_CUBIC: + // Need to implement a 3D bi-cubic filter (3x3x3) + return E_NOTIMPL; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );; + } +} + +HRESULT GenerateMipMaps3D( const Image* srcImages, size_t nimages, const TexMetadata& metadata, + DWORD filter, size_t levels, ScratchImage& mipChain ) +{ + if ( !srcImages || !nimages || !IsValid(metadata.format) + || !ispow2(metadata.width) || !ispow2(metadata.height) || !ispow2(metadata.depth) ) + return E_INVALIDARG; + + if ( metadata.dimension != TEX_DIMENSION_TEXTURE3D + || IsCompressed( metadata.format ) || IsVideo( metadata.format ) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + if ( !_CalculateMipLevels3D(metadata.width, metadata.height, metadata.depth, levels) ) + return E_INVALIDARG; + + std::vector baseImages; + baseImages.reserve( metadata.depth ); + for( size_t slice=0; slice < metadata.depth; ++slice ) + { + size_t index = metadata.ComputeIndex( 0, 0, slice ); + if ( index >= nimages ) + return E_FAIL; + + const Image& src = srcImages[ index ]; + if ( !src.pixels ) + return E_POINTER; + + if ( src.format != metadata.format || src.width != metadata.width || src.height != metadata.height ) + { + // All base images must be the same format, width, and height + return E_FAIL; + } + + baseImages.push_back( src ); + } + + assert( baseImages.size() == metadata.depth ); + + HRESULT hr; + + static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" ); + switch( filter & TEX_FILTER_MASK ) + { + case 0: + case TEX_FILTER_FANT: + hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain ); + if ( FAILED(hr) ) + return hr; + + // For decimation, Fant is equivalent to a Box filter + hr = _Generate3DMipsBoxFilter( metadata.depth, levels, mipChain ); + if ( FAILED(hr) ) + mipChain.Release(); + return hr; + + case WIC_FLAGS_FILTER_POINT: + hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain ); + if ( FAILED(hr) ) + return hr; + + hr = _Generate3DMipsPointFilter( metadata.depth, levels, mipChain ); + if ( FAILED(hr) ) + mipChain.Release(); + return hr; + + case WIC_FLAGS_FILTER_LINEAR: + // Need to implement a 3D bi-linear filter (2x2x2) + return E_NOTIMPL; + + case WIC_FLAGS_FILTER_CUBIC: + // Need to implement a 3D bi-cubic filter (3x3x3) + return E_NOTIMPL; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );; + } +} + +}; // namespace diff --git a/DirectXTex/DirectXTexMisc.cpp b/DirectXTex/DirectXTexMisc.cpp new file mode 100644 index 0000000..cddf734 --- /dev/null +++ b/DirectXTex/DirectXTexMisc.cpp @@ -0,0 +1,265 @@ +//------------------------------------------------------------------------------------- +// DirectXTexMisc.cpp +// +// DirectX Texture Library - Misc image operations +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +namespace DirectX +{ + +//------------------------------------------------------------------------------------- +static HRESULT _ComputeMSE( _In_ const Image& image1, _In_ const Image& image2, + _Out_ float& mse, _Out_opt_cap_c_(4) float* mseV ) +{ + if ( !image1.pixels || !image2.pixels ) + return E_POINTER; + + assert( image1.width == image2.width && image1.height == image2.height ); + assert( !IsCompressed( image1.format ) && !IsCompressed( image2.format ) ); + + const size_t width = image1.width; + + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*width)*2, 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + const uint8_t *pSrc1 = image1.pixels; + const size_t rowPitch1 = image1.rowPitch; + + const uint8_t *pSrc2 = image2.pixels; + const size_t rowPitch2 = image2.rowPitch; + + XMVECTOR acc = XMVectorZero(); + + for( size_t h = 0; h < image1.height; ++h ) + { + XMVECTOR* ptr1 = scanline.get(); + if ( !_LoadScanline( ptr1, width, pSrc1, rowPitch1, image1.format ) ) + return E_FAIL; + + XMVECTOR* ptr2 = scanline.get() + width; + if ( !_LoadScanline( ptr2, width, pSrc2, rowPitch2, image2.format ) ) + return E_FAIL; + + for( size_t i = 0; i < width; ++i, ++ptr1, ++ptr2 ) + { + // sum[ (I1 - I2)^2 ] + XMVECTOR v = XMVectorSubtract( *ptr1, *ptr2 ); + acc = XMVectorMultiplyAdd( v, v, acc ); + } + + pSrc1 += rowPitch1; + pSrc2 += rowPitch2; + } + + // MSE = sum[ (I1 - I2)^2 ] / w*h + XMVECTOR d = XMVectorReplicate( float(image1.width * image1.height) ); + XMVECTOR v = XMVectorDivide( acc, d ); + if ( mseV ) + { + XMStoreFloat4( reinterpret_cast( mseV ), v ); + mse = mseV[0] + mseV[1] + mseV[2] + mseV[3]; + } + else + { + XMFLOAT4 _mseV; + XMStoreFloat4( &_mseV, v ); + mse = _mseV.x + _mseV.y + _mseV.z + _mseV.w; + } + + return S_OK; +} + + +//===================================================================================== +// Entry points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Copies a rectangle from one image into another +//------------------------------------------------------------------------------------- +HRESULT CopyRectangle( const Image& srcImage, const Rect& srcRect, const Image& dstImage, DWORD filter, size_t xOffset, size_t yOffset ) +{ + if ( !srcImage.pixels || !dstImage.pixels ) + return E_POINTER; + + if ( IsCompressed( srcImage.format ) || IsCompressed( dstImage.format ) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + // Validate rectangle/offset + if ( !srcRect.w || !srcRect.h || ( (srcRect.x + srcRect.w) > srcImage.width ) || ( (srcRect.y + srcRect.h) > srcImage.height ) ) + { + return E_INVALIDARG; + } + + if ( ( (xOffset + srcRect.w) > dstImage.width ) || ( (yOffset + srcRect.h) > dstImage.height ) ) + { + return E_INVALIDARG; + } + + // Compute source bytes-per-pixel + size_t sbpp = BitsPerPixel( srcImage.format ); + if ( !sbpp ) + return E_FAIL; + + if ( sbpp < 8 ) + { + // We don't support monochrome (DXGI_FORMAT_R1_UNORM) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + const uint8_t* pEndSrc = srcImage.pixels + srcImage.rowPitch*srcImage.height; + const uint8_t* pEndDest = dstImage.pixels + dstImage.rowPitch*dstImage.height; + + // Round to bytes + sbpp = ( sbpp + 7 ) / 8; + + const uint8_t* pSrc = srcImage.pixels + (srcRect.y * srcImage.rowPitch) + (srcRect.x * sbpp); + + if ( srcImage.format == dstImage.format ) + { + // Direct copy case (avoid intermediate conversions) + uint8_t* pDest = dstImage.pixels + (yOffset * dstImage.rowPitch) + (xOffset * sbpp); + const size_t copyW = srcRect.w * sbpp; + for( size_t h=0; h < srcRect.h; ++h ) + { + if ( ( (pSrc+copyW) > pEndSrc ) || (pDest > pEndDest) ) + return E_FAIL; + + memcpy_s( pDest, pEndDest - pDest, pSrc, copyW ); + + pSrc += srcImage.rowPitch; + pDest += dstImage.rowPitch; + } + + return S_OK; + } + + // Compute destination bytes-per-pixel (not the same format as source) + size_t dbpp = BitsPerPixel( dstImage.format ); + if ( !dbpp ) + return E_FAIL; + + if ( dbpp < 8 ) + { + // We don't support monochrome (DXGI_FORMAT_R1_UNORM) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + // Round to bytes + dbpp = ( dbpp + 7 ) / 8; + + uint8_t* pDest = dstImage.pixels + (yOffset * dstImage.rowPitch) + (xOffset * dbpp); + + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*srcRect.w), 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + const size_t copyS = srcRect.w * sbpp; + const size_t copyD = srcRect.w * dbpp; + + for( size_t h=0; h < srcRect.h; ++h ) + { + if ( ( (pSrc+copyS) > pEndSrc) || ((pDest+copyD) > pEndDest) ) + return E_FAIL; + + if ( !_LoadScanline( scanline.get(), srcRect.w, pSrc, copyS, srcImage.format ) ) + return E_FAIL; + + _ConvertScanline( scanline.get(), srcRect.w, dstImage.format, srcImage.format, filter ); + + if ( !_StoreScanline( pDest, copyD, dstImage.format, scanline.get(), srcRect.w ) ) + return E_FAIL; + + pSrc += srcImage.rowPitch; + pDest += dstImage.rowPitch; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Computes the Mean-Squared-Error (MSE) between two images +//------------------------------------------------------------------------------------- +HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float* mseV ) +{ + if ( !image1.pixels || !image2.pixels ) + return E_POINTER; + + if ( image1.width != image2.width || image1.height != image2.height ) + return E_INVALIDARG; + + if ( IsCompressed(image1.format) ) + { + if ( IsCompressed(image2.format) ) + { + // Case 1: both images are compressed, expand to RGBA32F + ScratchImage temp1; + HRESULT hr = Decompress( image1, DXGI_FORMAT_R32G32B32A32_FLOAT, temp1 ); + if ( FAILED(hr) ) + return hr; + + ScratchImage temp2; + hr = Decompress( image2, DXGI_FORMAT_R32G32B32A32_FLOAT, temp2 ); + if ( FAILED(hr) ) + return hr; + + const Image* img1 = temp1.GetImage(0,0,0); + const Image* img2 = temp2.GetImage(0,0,0); + if ( !img1 || !img2 ) + return E_POINTER; + + return _ComputeMSE( *img1, *img2, mse, mseV ); + } + else + { + // Case 2: image1 is compressed, expand to RGBA32F + ScratchImage temp; + HRESULT hr = Decompress( image1, DXGI_FORMAT_R32G32B32A32_FLOAT, temp ); + if ( FAILED(hr) ) + return hr; + + const Image* img = temp.GetImage(0,0,0); + if ( !img ) + return E_POINTER; + + return _ComputeMSE( *img, image2, mse, mseV ); + } + } + else + { + if ( IsCompressed(image2.format) ) + { + // Case 3: image2 is compressed, expand to RGBA32F + ScratchImage temp; + HRESULT hr = Decompress( image2, DXGI_FORMAT_R32G32B32A32_FLOAT, temp ); + if ( FAILED(hr) ) + return hr; + + const Image* img = temp.GetImage(0,0,0); + if ( !img ) + return E_POINTER; + + return _ComputeMSE( image1, *img, mse, mseV ); + } + else + { + // Case 4: neither image is compressed + return _ComputeMSE( image1, image2, mse, mseV ); + } + } +} + +}; // namespace diff --git a/DirectXTex/DirectXTexNormalMaps.cpp b/DirectXTex/DirectXTexNormalMaps.cpp new file mode 100644 index 0000000..19d5a95 --- /dev/null +++ b/DirectXTex/DirectXTexNormalMaps.cpp @@ -0,0 +1,377 @@ +//------------------------------------------------------------------------------------- +// DirectXTexNormalMaps.cpp +// +// DirectX Texture Library - Normal map operations +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +namespace DirectX +{ + +#pragma prefast(suppress : 25000, "FXMVECTOR is 16 bytes") +static inline float _EvaluateColor( _In_ FXMVECTOR val, _In_ DWORD flags ) +{ + XMFLOAT4A f; + + static XMVECTORF32 lScale = { 0.2125f, 0.7154f, 0.0721f, 1.f }; + + static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" ); + switch( flags & 0xf ) + { + case 0: + case CNMAP_CHANNEL_RED: return XMVectorGetX( val ); + case CNMAP_CHANNEL_GREEN: return XMVectorGetY( val ); + case CNMAP_CHANNEL_BLUE: return XMVectorGetZ( val ); + case CNMAP_CHANNEL_ALPHA: return XMVectorGetW( val ); + + case CNMAP_CHANNEL_LUMINANCE: + { + XMVECTOR v = XMVectorMultiply( val, lScale ); + XMStoreFloat4A( &f, v ); + return f.x + f.y + f.z; + } + break; + + default: + assert(false); + return 0.f; + } +} + +static void _EvaluateRow( _In_count_(width) const XMVECTOR* pSource, _Out_cap_(width+2) float* pDest, + _In_ size_t width, _In_ DWORD flags ) +{ + assert( pSource && pDest ); + assert( width > 0 ); + + for( size_t x = 0; x < width; ++x ) + { + pDest[x+1] = _EvaluateColor( pSource[x], flags ); + } + + if ( flags & CNMAP_MIRROR_U ) + { + // Mirror in U + pDest[0] = _EvaluateColor( pSource[0], flags ); + pDest[width+1] = _EvaluateColor( pSource[width-1], flags ); + } + else + { + // Wrap in U + pDest[0] = _EvaluateColor( pSource[width-1], flags ); + pDest[width+1] = _EvaluateColor( pSource[0], flags ); + } +} + +static HRESULT _ComputeNMap( _In_ const Image& srcImage, _In_ DWORD flags, _In_ float amplitude, + _In_ DXGI_FORMAT format, _In_ const Image& normalMap ) +{ + if ( !srcImage.pixels || !normalMap.pixels ) + return E_INVALIDARG; + + assert( !IsCompressed(format) && !IsTypeless( format ) ); + + const DWORD convFlags = _GetConvertFlags( format ); + if ( !convFlags ) + return E_FAIL; + + if ( !( convFlags & (CONVF_UNORM | CONVF_SNORM | CONVF_FLOAT) ) ) + HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + const size_t width = srcImage.width; + const size_t height = srcImage.height; + if ( width != normalMap.width || height != normalMap.height ) + return E_FAIL; + + // Allocate temporary space (4 scanlines and 3 evaluated rows) + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*width*4), 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + ScopedAlignedArrayFloat buffer( reinterpret_cast( _aligned_malloc( ( ( sizeof(float) * ( width + 2 ) ) * 3 ), 16 ) ) ); + if ( !buffer ) + return E_OUTOFMEMORY; + + uint8_t* pDest = normalMap.pixels; + if ( !pDest ) + return E_POINTER; + + XMVECTOR* row0 = scanline.get(); + XMVECTOR* row1 = row0 + width; + XMVECTOR* row2 = row1 + width; + XMVECTOR* target = row2 + width; + + float* val0 = buffer.get(); + float* val1 = val0 + width + 2; + float* val2 = val1 + width + 2; + + const size_t rowPitch = srcImage.rowPitch; + const uint8_t* pSrc = srcImage.pixels; + + // Read first scanline row into 'row1' + if ( !_LoadScanline( row1, width, pSrc, rowPitch, srcImage.format ) ) + return E_FAIL; + + // Setup 'row0' + if ( flags & CNMAP_MIRROR_V ) + { + // Mirror first row + memcpy_s( row0, rowPitch, row1, rowPitch ); + } + else + { + // Read last row (Wrap V) + if ( !_LoadScanline( row0, width, pSrc + (rowPitch * (height-1)), rowPitch, srcImage.format ) ) + return E_FAIL; + } + + // Evaluate the initial rows + _EvaluateRow( row0, val0, width, flags ); + _EvaluateRow( row1, val1, width, flags ); + + pSrc += rowPitch; + + for( size_t y = 0; y < height; ++y ) + { + // Load next scanline of source image + if ( y < (height-1) ) + { + if ( !_LoadScanline( row2, width, pSrc, rowPitch, srcImage.format ) ) + return E_FAIL; + } + else + { + if ( flags & CNMAP_MIRROR_V ) + { + // Use last row of source image + if ( !_LoadScanline( row2, width, srcImage.pixels + (rowPitch * (height-1)), rowPitch, srcImage.format ) ) + return E_FAIL; + } + else + { + // Use first row of source image (Wrap V) + if ( !_LoadScanline( row2, width, srcImage.pixels, rowPitch, srcImage.format ) ) + return E_FAIL; + } + } + + // Evaluate row + _EvaluateRow( row2, val2, width, flags ); + + // Generate target scanline + XMVECTOR *dptr = target; + for( size_t x = 0; x < width; ++x ) + { + // Compute normal via central differencing + float totDelta = ( val0[x] - val0[x+2] ) + ( val1[x] - val1[x+2] ) + ( val2[x] - val2[x+2] ); + float deltaZX = totDelta * amplitude / 6.f; + + totDelta = ( val0[x] - val2[x] ) + ( val0[x+1] - val2[x+1] ) + ( val0[x+2] - val2[x+2] ); + float deltaZY = totDelta * amplitude / 6.f; + + XMVECTOR vx = XMVectorSetZ( g_XMNegIdentityR0, deltaZX ); // (-1.0f, 0.0f, deltaZX) + XMVECTOR vy = XMVectorSetZ( g_XMNegIdentityR1, deltaZY ); // (0.0f, -1.0f, deltaZY) + + XMVECTOR normal = XMVector3Normalize( XMVector3Cross( vx, vy ) ); + + // Compute alpha (1.0 or an occlusion term) + float alpha = 1.f; + + if ( flags & CNMAP_COMPUTE_OCCLUSION ) + { + float delta = 0.f; + float c = val1[x+1]; + + float t = val0[x] - c; if ( t > 0.f ) delta += t; + t = val0[x+1] - c; if ( t > 0.f ) delta += t; + t = val0[x+2] - c; if ( t > 0.f ) delta += t; + t = val1[x] - c; if ( t > 0.f ) delta += t; + // Skip current pixel + t = val1[x+2] - c; if ( t > 0.f ) delta += t; + t = val2[x] - c; if ( t > 0.f ) delta += t; + t = val2[x+1] - c; if ( t > 0.f ) delta += t; + t = val2[x+2] - c; if ( t > 0.f ) delta += t; + + // Average delta (divide by 8, scale by amplitude factor) + delta *= 0.125f * amplitude; + if ( delta > 0.f ) + { + // If < 0, then no occlusion + float r = sqrtf( 1.f + delta*delta ); + alpha = (r - delta) / r; + } + } + + // Encode based on target format + if ( convFlags & CONVF_UNORM ) + { + // 0.5f*normal + 0.5f -or- invert sign case: -0.5f*normal + 0.5f + XMVECTOR n1 = XMVectorMultiplyAdd( (flags & CNMAP_INVERT_SIGN) ? g_XMNegativeOneHalf : g_XMOneHalf, normal, g_XMOneHalf ); + *dptr++ = XMVectorSetW( n1, alpha ); + } + else if ( flags & CNMAP_INVERT_SIGN ) + { + *dptr++ = XMVectorSetW( XMVectorNegate( normal ), alpha ); + } + else + { + *dptr++ = XMVectorSetW( normal, alpha ); + } + } + + if ( !_StoreScanline( pDest, normalMap.rowPitch, format, target, width ) ) + return E_FAIL; + + // Cycle buffers + float* temp = val0; + val0 = val1; + val1 = val2; + val2 = temp; + + pSrc += rowPitch; + pDest += normalMap.rowPitch; + } + + return S_OK; +} + + +//===================================================================================== +// Entry points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Generates a normal map from a height-map +//------------------------------------------------------------------------------------- +HRESULT ComputeNormalMap( const Image& srcImage, DWORD flags, float amplitude, + DXGI_FORMAT format, ScratchImage& normalMap ) +{ + if ( !srcImage.pixels || !IsValid(format) || IsCompressed( format ) || IsTypeless( format ) ) + return E_INVALIDARG; + + static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" ); + switch( flags & 0xf ) + { + case 0: + case CNMAP_CHANNEL_RED: + case CNMAP_CHANNEL_GREEN: + case CNMAP_CHANNEL_BLUE: + case CNMAP_CHANNEL_ALPHA: + case CNMAP_CHANNEL_LUMINANCE: + break; + + default: + return E_INVALIDARG; + } + + if ( IsCompressed( srcImage.format ) || IsTypeless( srcImage.format ) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + // Setup target image + normalMap.Release(); + + HRESULT hr = normalMap.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *img = normalMap.GetImage( 0, 0, 0 ); + if ( !img ) + { + normalMap.Release(); + return E_POINTER; + } + + hr = _ComputeNMap( srcImage, flags, amplitude, format, *img ); + if ( FAILED(hr) ) + { + normalMap.Release(); + return hr; + } + + return S_OK; +} + +HRESULT ComputeNormalMap( const Image* srcImages, size_t nimages, const TexMetadata& metadata, + DWORD flags, float amplitude, DXGI_FORMAT format, ScratchImage& normalMaps ) +{ + if ( !srcImages || !nimages ) + return E_INVALIDARG; + + if ( !IsValid(format) || IsCompressed(format) || IsTypeless(format) ) + return E_INVALIDARG; + + static_assert( CNMAP_CHANNEL_RED == 0x1, "CNMAP_CHANNEL_ flag values don't match mask" ); + switch( flags & 0xf ) + { + case 0: + case CNMAP_CHANNEL_RED: + case CNMAP_CHANNEL_GREEN: + case CNMAP_CHANNEL_BLUE: + case CNMAP_CHANNEL_ALPHA: + case CNMAP_CHANNEL_LUMINANCE: + break; + + default: + return E_INVALIDARG; + } + + normalMaps.Release(); + + TexMetadata mdata2 = metadata; + mdata2.format = format; + HRESULT hr = normalMaps.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + if ( nimages != normalMaps.GetImageCount() ) + { + normalMaps.Release(); + return E_FAIL; + } + + const Image* dest = normalMaps.GetImages(); + if ( !dest ) + { + normalMaps.Release(); + return E_POINTER; + } + + for( size_t index=0; index < nimages; ++index ) + { + assert( dest[ index ].format == format ); + + const Image& src = srcImages[ index ]; + if ( IsCompressed( src.format ) || IsTypeless( src.format ) ) + { + normalMaps.Release(); + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + if ( src.width != dest[ index ].width || src.height != dest[ index ].height ) + { + normalMaps.Release(); + return E_FAIL; + } + + hr = _ComputeNMap( src, flags, amplitude, format, dest[ index ] ); + if ( FAILED(hr) ) + { + normalMaps.Release(); + return hr; + } + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexP.h b/DirectXTex/DirectXTexP.h new file mode 100644 index 0000000..9665c08 --- /dev/null +++ b/DirectXTex/DirectXTexP.h @@ -0,0 +1,209 @@ +//------------------------------------------------------------------------------------- +// DirectXTexp.h +// +// DirectX Texture Library - Private header +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#if defined(_MSC_VER) && (_MSC_VER > 1000) +#pragma once +#endif + +#define NOMINMAX +#include + +#ifdef USE_XNAMATH +#include +#else +#include +#include +#endif + +#include + +#include +#include + +#include + +#include +#include + +#include + +#pragma warning(push) +#pragma warning(disable : 4005) +#include +#pragma warning(pop) + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) && !defined(DXGI_1_2_FORMATS) +#define DXGI_1_2_FORMATS +#endif + +#include "directxtex.h" + +#include "scoped.h" + +struct IWICImagingFactory; + +#define TEX_FILTER_MASK 0xF00000 + +namespace DirectX +{ + +#ifdef USE_XNAMATH + template + inline XMVECTOR XMVectorSwizzle(FXMVECTOR V) + { + return XMVectorSwizzle( V, SwizzleX, SwizzleY, SwizzleZ, SwizzleW ); + } + + template + inline XMVECTOR XMVectorPermute(FXMVECTOR V1, FXMVECTOR V2) + { + static XMVECTORI32 control = { PermuteX, PermuteY, PermuteZ, PermuteW }; + return XMVectorPermute( V1, V2, control ); + } +#endif // USE_XNAMATH + + //--------------------------------------------------------------------------------- + // WIC helper functions + DXGI_FORMAT _WICToDXGI( _In_ const GUID& guid ); + bool _DXGIToWIC( _In_ DXGI_FORMAT format, _Out_ GUID& guid ); + + size_t _WICBitsPerPixel( _In_ REFGUID targetGuid ); + + IWICImagingFactory* _GetWIC(); + + inline WICBitmapDitherType _GetWICDither( _In_ DWORD flags ) + { + static_assert( TEX_FILTER_DITHER == 0x10000, "TEX_FILTER_DITHER* flag values don't match mask" ); + + static_assert( TEX_FILTER_DITHER == WIC_FLAGS_DITHER, "TEX_FILTER_DITHER* should match WIC_FLAGS_DITHER*" ); + static_assert( TEX_FILTER_DITHER_DIFFUSION == WIC_FLAGS_DITHER_DIFFUSION, "TEX_FILTER_DITHER* should match WIC_FLAGS_DITHER*" ); + + switch( flags & 0xF0000 ) + { + case TEX_FILTER_DITHER: + return WICBitmapDitherTypeOrdered4x4; + + case TEX_FILTER_DITHER_DIFFUSION: + return WICBitmapDitherTypeErrorDiffusion; + + default: + return WICBitmapDitherTypeNone; + } + } + + inline WICBitmapInterpolationMode _GetWICInterp( _In_ DWORD flags ) + { + static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" ); + + static_assert( TEX_FILTER_POINT == WIC_FLAGS_FILTER_POINT, "TEX_FILTER_* flags should match WIC_FLAGS_FILTER_*" ); + static_assert( TEX_FILTER_LINEAR == WIC_FLAGS_FILTER_LINEAR, "TEX_FILTER_* flags should match WIC_FLAGS_FILTER_*" ); + static_assert( TEX_FILTER_CUBIC == WIC_FLAGS_FILTER_CUBIC, "TEX_FILTER_* flags should match WIC_FLAGS_FILTER_*" ); + static_assert( TEX_FILTER_FANT == WIC_FLAGS_FILTER_FANT, "TEX_FILTER_* flags should match WIC_FLAGS_FILTER_*" ); + + switch( flags & TEX_FILTER_MASK ) + { + case TEX_FILTER_POINT: + return WICBitmapInterpolationModeNearestNeighbor; + + case TEX_FILTER_LINEAR: + return WICBitmapInterpolationModeLinear; + + case TEX_FILTER_CUBIC: + return WICBitmapInterpolationModeCubic; + + case TEX_FILTER_FANT: + default: + return WICBitmapInterpolationModeFant; + } + } + + //--------------------------------------------------------------------------------- + // Image helper functions + void _DetermineImageArray( _In_ const TexMetadata& metadata, _In_ DWORD cpFlags, + _Out_ size_t& nImages, _Out_ size_t& pixelSize ); + + bool _SetupImageArray( _In_bytecount_(pixelSize) uint8_t *pMemory, _In_ size_t pixelSize, + _In_ const TexMetadata& metadata, _In_ DWORD cpFlags, + _Out_cap_(nImages) Image* images, _In_ size_t nImages ); + + //--------------------------------------------------------------------------------- + // Conversion helper functions + + enum TEXP_SCANLINE_FLAGS + { + TEXP_SCANLINE_NONE = 0, + TEXP_SCANLINE_SETALPHA = 0x1, // Set alpha channel to known opaque value + TEXP_SCANLINE_LEGACY = 0x2, // Enables specific legacy format conversion cases + }; + + enum CONVERT_FLAGS + { + CONVF_FLOAT = 0x1, + CONVF_UNORM = 0x2, + CONVF_UINT = 0x4, + CONVF_SNORM = 0x8, + CONVF_SINT = 0x10, + CONVF_DEPTH = 0x20, + CONVF_STENCIL = 0x40, + CONVF_SHAREDEXP = 0x80, + CONVF_BGR = 0x100, + CONVF_X2 = 0x200, + CONVF_PACKED = 0x400, + CONVF_BC = 0x800, + CONVF_R = 0x10000, + CONVF_G = 0x20000, + CONVF_B = 0x40000, + CONVF_A = 0x80000, + CONVF_RGB_MASK = 0x70000, + CONVF_RGBA_MASK = 0xF0000, + }; + + DWORD _GetConvertFlags( _In_ DXGI_FORMAT format ); + + void _CopyScanline( _Out_bytecap_(outSize) LPVOID pDestination, _In_ size_t outSize, + _In_bytecount_(inSize) LPCVOID pSource, _In_ size_t inSize, + _In_ DXGI_FORMAT format, _In_ DWORD flags ); + + void _SwizzleScanline( _Out_bytecap_(outSize) LPVOID pDestination, _In_ size_t outSize, + _In_bytecount_(inSize) LPCVOID pSource, _In_ size_t inSize, + _In_ DXGI_FORMAT format, _In_ DWORD flags ); + + bool _ExpandScanline( _Out_bytecap_(outSize) LPVOID pDestination, _In_ size_t outSize, + _In_ DXGI_FORMAT outFormat, + _In_bytecount_(inSize) LPCVOID pSource, _In_ size_t inSize, + _In_ DXGI_FORMAT inFormat, _In_ DWORD flags ); + + bool _LoadScanline( _Out_cap_(count) XMVECTOR* pDestination, _In_ size_t count, + _In_bytecount_(size) LPCVOID pSource, _In_ size_t size, _In_ DXGI_FORMAT format ); + + bool _StoreScanline( _Out_bytecap_(size) LPVOID pDestination, _In_ size_t size, _In_ DXGI_FORMAT format, + _In_count_(count) const XMVECTOR* pSource, _In_ size_t count ); + + HRESULT _ConvertToR32G32B32A32( _In_ const Image& srcImage, _Inout_ ScratchImage& image ); + + HRESULT _ConvertFromR32G32B32A32( _In_ const Image& srcImage, _In_ const Image& destImage ); + HRESULT _ConvertFromR32G32B32A32( _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _Inout_ ScratchImage& image ); + HRESULT _ConvertFromR32G32B32A32( _In_count_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DXGI_FORMAT format, _Out_ ScratchImage& result ); + + void _ConvertScanline( _Inout_count_(count) XMVECTOR* pBuffer, _In_ size_t count, + _In_ DXGI_FORMAT outFormat, _In_ DXGI_FORMAT inFormat, _In_ DWORD flags ); + + //--------------------------------------------------------------------------------- + // DDS helper functions + HRESULT _EncodeDDSHeader( _In_ const TexMetadata& metadata, DWORD flags, + _Out_opt_cap_x_(maxsize) LPVOID pDestination, _In_ size_t maxsize, _Out_ size_t& required ); + +}; // namespace diff --git a/DirectXTex/DirectXTexResize.cpp b/DirectXTex/DirectXTexResize.cpp new file mode 100644 index 0000000..7952ae4 --- /dev/null +++ b/DirectXTex/DirectXTexResize.cpp @@ -0,0 +1,358 @@ +//------------------------------------------------------------------------------------- +// DirectXTexResize.cpp +// +// DirectX Texture Library - Image resizing operations +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +namespace DirectX +{ + +extern HRESULT _ResizeSeparateColorAndAlpha( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* original, + _In_ size_t newWidth, _In_ size_t newHeight, _In_ DWORD filter, _Inout_ const Image* img ); + +//------------------------------------------------------------------------------------- +// Do image resize using WIC +//------------------------------------------------------------------------------------- +static HRESULT _PerformResizeUsingWIC( _In_ const Image& srcImage, _In_ DWORD filter, + _In_ const WICPixelFormatGUID& pfGUID, _In_ const Image& destImage ) +{ + if ( !srcImage.pixels || !destImage.pixels ) + return E_POINTER; + + assert( srcImage.format == destImage.format ); + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject componentInfo; + HRESULT hr = pWIC->CreateComponentInfo( pfGUID, &componentInfo ); + if ( FAILED(hr) ) + return hr; + + ScopedObject pixelFormatInfo; + hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo2), (void**)&pixelFormatInfo ); + if ( FAILED(hr) ) + return hr; + + BOOL supportsTransparency = FALSE; + hr = pixelFormatInfo->SupportsTransparency( &supportsTransparency ); + if ( FAILED(hr) ) + return hr; + + ScopedObject source; + hr = pWIC->CreateBitmapFromMemory( static_cast( srcImage.width ), static_cast( srcImage.height ), pfGUID, + static_cast( srcImage.rowPitch ), static_cast( srcImage.slicePitch ), + srcImage.pixels, &source ); + if ( FAILED(hr) ) + return hr; + + if ( (filter & TEX_FILTER_SEPARATE_ALPHA) && supportsTransparency ) + { + hr = _ResizeSeparateColorAndAlpha( pWIC, source.Get(), destImage.width, destImage.height, filter, &destImage ); + if ( FAILED(hr) ) + return hr; + } + else + { + ScopedObject scaler; + hr = pWIC->CreateBitmapScaler( &scaler ); + if ( FAILED(hr) ) + return hr; + + hr = scaler->Initialize( source.Get(), static_cast( destImage.width ), static_cast( destImage.height ), _GetWICInterp( filter ) ); + if ( FAILED(hr) ) + return hr; + + WICPixelFormatGUID pfScaler; + hr = scaler->GetPixelFormat( &pfScaler ); + if ( FAILED(hr) ) + return hr; + + if ( memcmp( &pfScaler, &pfGUID, sizeof(WICPixelFormatGUID) ) == 0 ) + { + hr = scaler->CopyPixels( 0, static_cast( destImage.rowPitch ), static_cast( destImage.slicePitch ), destImage.pixels ); + if ( FAILED(hr) ) + return hr; + } + else + { + // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we + // convert it back + ScopedObject FC; + hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( scaler.Get(), pfGUID, _GetWICDither( filter ), 0, 0, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( destImage.rowPitch ), static_cast( destImage.slicePitch ), destImage.pixels ); + if ( FAILED(hr) ) + return hr; + } + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Do conversion, resize using WIC, conversion cycle +//------------------------------------------------------------------------------------- +static HRESULT _PerformResizeViaF32( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage ) +{ + if ( !srcImage.pixels || !destImage.pixels ) + return E_POINTER; + + assert( srcImage.format != DXGI_FORMAT_R32G32B32A32_FLOAT ); + assert( srcImage.format == destImage.format ); + + ScratchImage temp; + HRESULT hr = _ConvertToR32G32B32A32( srcImage, temp ); + if ( FAILED(hr) ) + return hr; + + const Image *tsrc = temp.GetImage( 0, 0, 0 ); + if ( !tsrc ) + return E_POINTER; + + ScratchImage rtemp; + hr = rtemp.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, destImage.width, destImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *tdest = rtemp.GetImage( 0, 0, 0 ); + if ( !tdest ) + return E_POINTER; + + hr = _PerformResizeUsingWIC( *tsrc, filter, GUID_WICPixelFormat128bppRGBAFloat, *tdest ); + if ( FAILED(hr) ) + return hr; + + temp.Release(); + + hr = _ConvertFromR32G32B32A32( *tdest, destImage ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Resize image +//------------------------------------------------------------------------------------- +HRESULT Resize( const Image& srcImage, size_t width, size_t height, DWORD filter, ScratchImage& image ) +{ + if ( width == 0 || height == 0 ) + return E_INVALIDARG; + +#ifdef _AMD64_ + if ( (srcImage.width > 0xFFFFFFFF) || (srcImage.height > 0xFFFFFFFF) ) + return E_INVALIDARG; + + if ( (width > 0xFFFFFFFF) || (height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + if ( !srcImage.pixels ) + return E_POINTER; + + if ( IsCompressed( srcImage.format ) ) + { + // We don't support resizing compressed images + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + HRESULT hr = image.Initialize2D( srcImage.format, width, height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *rimage = image.GetImage( 0, 0, 0 ); + if ( !rimage ) + return E_POINTER; + + // WIC only supports CLAMP + + WICPixelFormatGUID pfGUID; + if ( _DXGIToWIC( srcImage.format, pfGUID ) ) + { + // Case 1: Source format is supported by Windows Imaging Component + hr = _PerformResizeUsingWIC( srcImage, filter, pfGUID, *rimage ); + } + else + { + // Case 2: Source format is not supported by WIC, so we have to convert, resize, and convert back + hr = _PerformResizeViaF32( srcImage, filter, *rimage ); + } + + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Resize image (complex) +//------------------------------------------------------------------------------------- +HRESULT Resize( const Image* srcImages, size_t nimages, const TexMetadata& metadata, + size_t width, size_t height, DWORD filter, ScratchImage& result ) +{ + if ( !srcImages || !nimages || width == 0 || height == 0 ) + return E_INVALIDARG; + +#ifdef _AMD64_ + if ( (width > 0xFFFFFFFF) || (height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + TexMetadata mdata2 = metadata; + mdata2.width = width; + mdata2.height = height; + mdata2.mipLevels = 1; + HRESULT hr = result.Initialize( mdata2 ); + if ( FAILED(hr) ) + return hr; + + WICPixelFormatGUID pfGUID; + bool wicpf = _DXGIToWIC( metadata.format, pfGUID ); + + switch ( metadata.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + case TEX_DIMENSION_TEXTURE2D: + assert( metadata.depth == 1 ); + + for( size_t item = 0; item < metadata.arraySize; ++item ) + { + size_t srcIndex = metadata.ComputeIndex( 0, item, 0 ); + if ( srcIndex >= nimages ) + { + result.Release(); + return E_FAIL; + } + + const Image* srcimg = &srcImages[ srcIndex ]; + const Image* destimg = result.GetImage( 0, item, 0 ); + if ( !srcimg || !destimg ) + { + result.Release(); + return E_POINTER; + } + + if ( srcimg->format != metadata.format ) + { + result.Release(); + return E_FAIL; + } + +#ifdef _AMD64_ + if ( (srcimg->width > 0xFFFFFFFF) || (srcimg->height > 0xFFFFFFFF) ) + { + result.Release(); + return E_FAIL; + } +#endif + + if ( wicpf ) + { + // Case 1: Source format is supported by Windows Imaging Component + hr = _PerformResizeUsingWIC( *srcimg, filter, pfGUID, *destimg ); + } + else + { + // Case 2: Source format is not supported by WIC, so we have to convert, resize, and convert back + hr = _PerformResizeViaF32( *srcimg, filter, *destimg ); + } + + if ( FAILED(hr) ) + { + result.Release(); + return hr; + } + } + break; + + case TEX_DIMENSION_TEXTURE3D: + assert( metadata.arraySize == 1 ); + + for( size_t slice = 0; slice < metadata.depth; ++slice ) + { + size_t srcIndex = metadata.ComputeIndex( 0, 0, slice ); + if ( srcIndex >= nimages ) + { + result.Release(); + return E_FAIL; + } + + const Image* srcimg = &srcImages[ srcIndex ]; + const Image* destimg = result.GetImage( 0, 0, slice ); + if ( !srcimg || !destimg ) + { + result.Release(); + return E_POINTER; + } + + if ( srcimg->format != metadata.format ) + { + result.Release(); + return E_FAIL; + } + +#ifdef _AMD64_ + if ( (srcimg->width > 0xFFFFFFFF) || (srcimg->height > 0xFFFFFFFF) ) + { + result.Release(); + return E_FAIL; + } +#endif + + if ( wicpf ) + { + // Case 1: Source format is supported by Windows Imaging Component + hr = _PerformResizeUsingWIC( *srcimg, filter, pfGUID, *destimg ); + } + else + { + // Case 2: Source format is not supported by WIC, so we have to convert, resize, and convert back + hr = _PerformResizeViaF32( *srcimg, filter, *destimg ); + } + + if ( FAILED(hr) ) + { + result.Release(); + return hr; + } + } + break; + + default: + result.Release(); + return E_FAIL; + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexTGA.cpp b/DirectXTex/DirectXTexTGA.cpp new file mode 100644 index 0000000..f50be98 --- /dev/null +++ b/DirectXTex/DirectXTexTGA.cpp @@ -0,0 +1,1386 @@ +//------------------------------------------------------------------------------------- +// DirectXTexTGA.cpp +// +// DirectX Texture Library - Targa Truevision (TGA) file format reader/writer +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +// +// The implementation here has the following limitations: +// * Does not support files that contain color maps (these are rare in practice) +// * Interleaved files are not supported (deprecated aspect of TGA format) +// * Only supports 8-bit greyscale; 16-, 24-, and 32-bit truecolor images +// * Always writes uncompressed files (i.e. can read RLE compression, but does not write it) +// + +enum TGAImageType +{ + TGA_NO_IMAGE = 0, + TGA_COLOR_MAPPED = 1, + TGA_TRUECOLOR = 2, + TGA_BLACK_AND_WHITE = 3, + TGA_COLOR_MAPPED_RLE = 9, + TGA_TRUECOLOR_RLE = 10, + TGA_BLACK_AND_WHITE_RLE = 11, +}; + +enum TGADescriptorFlags +{ + TGA_FLAGS_INVERTX = 0x10, + TGA_FLAGS_INVERTY = 0x20, + TGA_FLAGS_INTERLEAVED_2WAY = 0x40, // Deprecated + TGA_FLAGS_INTERLEAVED_4WAY = 0x80, // Deprecated +}; + +const char* g_TGA20_Signature = "TRUEVISION-XFILE."; + +#pragma pack(push,1) +struct TGA_HEADER +{ + uint8_t bIDLength; + uint8_t bColorMapType; + uint8_t bImageType; + uint16_t wColorMapFirst; + uint16_t wColorMapLength; + uint8_t bColorMapSize; + uint16_t wXOrigin; + uint16_t wYOrigin; + uint16_t wWidth; + uint16_t wHeight; + uint8_t bBitsPerPixel; + uint8_t bDescriptor; +}; + +struct TGA_FOOTER +{ + uint16_t dwExtensionOffset; + uint16_t dwDeveloperOffset; + char Signature[18]; +}; + +struct TGA_EXTENSION +{ + uint16_t wSize; + char szAuthorName[41]; + char szAuthorComment[324]; + uint16_t wStampMonth; + uint16_t wStampDay; + uint16_t wStampYear; + uint16_t wStampHour; + uint16_t wStampMinute; + uint16_t wStampSecond; + char szJobName[41]; + uint16_t wJobHour; + uint16_t wJobMinute; + uint16_t wJobSecond; + char szSoftwareId[41]; + uint16_t wVersionNumber; + uint8_t bVersionLetter; + uint32_t dwKeyColor; + uint16_t wPixelNumerator; + uint16_t wPixelDenominator; + uint16_t wGammaNumerator; + uint16_t wGammaDenominator; + uint32_t dwColorOffset; + uint32_t dwStampOffset; + uint32_t dwScanOffset; + uint8_t bAttributesType; +}; +#pragma pack(pop) + +enum CONVERSION_FLAGS +{ + CONV_FLAGS_NONE = 0x0, + CONV_FLAGS_EXPAND = 0x1, // Conversion requires expanded pixel size + CONV_FLAGS_INVERTX = 0x2, // If set, scanlines are right-to-left + CONV_FLAGS_INVERTY = 0x4, // If set, scanlines are top-to-bottom + CONV_FLAGS_RLE = 0x8, // Source data is RLE compressed + + CONV_FLAGS_SWIZZLE = 0x10000, // Swizzle BGR<->RGB data + CONV_FLAGS_888 = 0x20000, // 24bpp format +}; + +namespace DirectX +{ + +//------------------------------------------------------------------------------------- +// Decodes TGA header +//------------------------------------------------------------------------------------- +static HRESULT _DecodeTGAHeader( _In_bytecount_(size) LPCVOID pSource, size_t size, _Out_ TexMetadata& metadata, size_t& offset, + _Inout_opt_ DWORD* convFlags ) +{ + if ( !pSource ) + return E_INVALIDARG; + + memset( &metadata, 0, sizeof(TexMetadata) ); + + if ( size < sizeof(TGA_HEADER) ) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + const TGA_HEADER* pHeader = reinterpret_cast( pSource ); + assert( pHeader ); + + if ( pHeader->bColorMapType != 0 + || pHeader->wColorMapLength != 0 ) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + if ( pHeader->bDescriptor & (TGA_FLAGS_INTERLEAVED_2WAY|TGA_FLAGS_INTERLEAVED_4WAY) ) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + if ( !pHeader->wWidth || !pHeader->wHeight ) + { + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + switch ( pHeader->bImageType ) + { + case TGA_TRUECOLOR: + case TGA_TRUECOLOR_RLE: + switch( pHeader->bBitsPerPixel ) + { + case 16: + metadata.format = DXGI_FORMAT_B5G5R5A1_UNORM; + break; + + case 24: + metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; + if ( convFlags ) + *convFlags |= CONV_FLAGS_EXPAND; + // We could use DXGI_FORMAT_B8G8R8X8_UNORM, but we prefer DXGI 1.0 formats + break; + + case 32: + metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; + // We could use DXGI_FORMAT_B8G8R8A8_UNORM, but we prefer DXGI 1.0 formats + break; + } + + if ( convFlags && (pHeader->bImageType == TGA_TRUECOLOR_RLE) ) + { + *convFlags |= CONV_FLAGS_RLE; + } + break; + + case TGA_BLACK_AND_WHITE: + case TGA_BLACK_AND_WHITE_RLE: + switch( pHeader->bBitsPerPixel ) + { + case 8: + metadata.format = DXGI_FORMAT_R8_UNORM; + break; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + if ( convFlags && (pHeader->bImageType == TGA_BLACK_AND_WHITE_RLE) ) + { + *convFlags |= CONV_FLAGS_RLE; + } + break; + + case TGA_NO_IMAGE: + case TGA_COLOR_MAPPED: + case TGA_COLOR_MAPPED_RLE: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + default: + return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); + } + + metadata.width = pHeader->wWidth; + metadata.height = pHeader->wHeight; + metadata.depth = metadata.arraySize = metadata.mipLevels = 1; + metadata.dimension = TEX_DIMENSION_TEXTURE2D; + + if ( convFlags ) + { + if ( pHeader->bDescriptor & TGA_FLAGS_INVERTX ) + *convFlags |= CONV_FLAGS_INVERTX; + + if ( pHeader->bDescriptor & TGA_FLAGS_INVERTY ) + *convFlags |= CONV_FLAGS_INVERTY; + } + + offset = sizeof( TGA_HEADER ); + + if ( pHeader->bIDLength != 0 ) + { + offset += pHeader->bIDLength; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Set alpha for images with all 0 alpha channel +//------------------------------------------------------------------------------------- +static HRESULT _SetAlphaChannelToOpaque( _In_ const Image* image ) +{ + assert( image ); + + uint8_t* pPixels = reinterpret_cast( image->pixels ); + if ( !pPixels ) + return E_POINTER; + + for( size_t y = 0; y < image->height; ++y ) + { + _CopyScanline( pPixels, image->rowPitch, pPixels, image->rowPitch, image->format, TEXP_SCANLINE_SETALPHA ); + pPixels += image->rowPitch; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Uncompress pixel data from a TGA into the target image +//------------------------------------------------------------------------------------- +static HRESULT _UncompressPixels( _In_bytecount_(size) LPCVOID pSource, size_t size, _In_ const Image* image, DWORD convFlags ) +{ + assert( pSource && size > 0 ); + + if ( !image || !image->pixels ) + return E_POINTER; + + // Compute TGA image data pitch + size_t rowPitch; + if ( convFlags & CONV_FLAGS_EXPAND ) + { + rowPitch = image->width * 3; + } + else + { + size_t slicePitch; + ComputePitch( image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE ); + } + + const uint8_t* sPtr = reinterpret_cast( pSource ); + const uint8_t* endPtr = sPtr + size; + + switch( image->format ) + { + //--------------------------------------------------------------------------- 8-bit + case DXGI_FORMAT_R8_UNORM: + for( size_t y=0; y < image->height; ++y ) + { + size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 ); + assert( offset < rowPitch); + + uint8_t* dPtr = reinterpret_cast( image->pixels ) + + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) + + offset; + + for( size_t x=0; x < image->width; ) + { + if ( sPtr >= endPtr ) + return E_FAIL; + + if ( *sPtr & 0x80 ) + { + // Repeat + size_t j = (*sPtr & 0x7F) + 1; + if ( ++sPtr >= endPtr ) + return E_FAIL; + + for( ; j > 0; --j, ++x ) + { + if ( x >= image->width ) + return E_FAIL; + + *dPtr = *sPtr; + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + + ++sPtr; + } + else + { + // Literal + size_t j = (*sPtr & 0x7F) + 1; + ++sPtr; + + if ( sPtr+j > endPtr ) + return E_FAIL; + + for( ; j > 0; --j, ++x ) + { + if ( x >= image->width ) + return E_FAIL; + + *dPtr = *(sPtr++); + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + } + } + break; + + //-------------------------------------------------------------------------- 16-bit + case DXGI_FORMAT_B5G5R5A1_UNORM: + { + bool nonzeroa = false; + for( size_t y=0; y < image->height; ++y ) + { + size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 ); + assert( offset*2 < rowPitch); + + uint16_t* dPtr = reinterpret_cast( reinterpret_cast( image->pixels ) + + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) ) + + offset; + + for( size_t x=0; x < image->width; ) + { + if ( sPtr >= endPtr ) + return E_FAIL; + + if ( *sPtr & 0x80 ) + { + // Repeat + size_t j = (*sPtr & 0x7F) + 1; + ++sPtr; + + if ( sPtr+1 >= endPtr ) + return E_FAIL; + + uint16_t t = *sPtr | (*(sPtr+1) << 8); + if ( t & 0x8000 ) + nonzeroa = true; + sPtr += 2; + + for( ; j > 0; --j, ++x ) + { + if ( x >= image->width ) + return E_FAIL; + + *dPtr = t; + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + else + { + // Literal + size_t j = (*sPtr & 0x7F) + 1; + ++sPtr; + + if ( sPtr+(j*2) > endPtr ) + return E_FAIL; + + for( ; j > 0; --j, ++x ) + { + if ( x >= image->width ) + return E_FAIL; + + uint16_t t = *sPtr | (*(sPtr+1) << 8); + if ( t & 0x8000 ) + nonzeroa = true; + sPtr += 2; + *dPtr = t; + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + } + } + + // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque + if ( !nonzeroa ) + { + HRESULT hr = _SetAlphaChannelToOpaque( image ); + if ( FAILED(hr) ) + return hr; + } + } + break; + + //----------------------------------------------------------------------- 24/32-bit + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + bool nonzeroa = false; + for( size_t y=0; y < image->height; ++y ) + { + size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 ); + + uint32_t* dPtr = reinterpret_cast( reinterpret_cast( image->pixels ) + + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) ) + + offset; + + for( size_t x=0; x < image->width; ) + { + if ( sPtr >= endPtr ) + return E_FAIL; + + if ( *sPtr & 0x80 ) + { + // Repeat + size_t j = (*sPtr & 0x7F) + 1; + ++sPtr; + + DWORD t; + if ( convFlags & CONV_FLAGS_EXPAND ) + { + assert( offset*3 < rowPitch); + + if ( sPtr+2 >= endPtr ) + return E_FAIL; + + // BGR -> RGBA + t = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000; + sPtr += 3; + + nonzeroa = true; + } + else + { + assert( offset*4 < rowPitch); + + if ( sPtr+3 >= endPtr ) + return E_FAIL; + + // BGRA -> RGBA + t = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 ); + + if ( *(sPtr+3) > 0 ) + nonzeroa = true; + + sPtr += 4; + } + + for( ; j > 0; --j, ++x ) + { + if ( x >= image->width ) + return E_FAIL; + + *dPtr = t; + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + else + { + // Literal + size_t j = (*sPtr & 0x7F) + 1; + ++sPtr; + + if ( convFlags & CONV_FLAGS_EXPAND ) + { + if ( sPtr+(j*3) > endPtr ) + return E_FAIL; + } + else + { + if ( sPtr+(j*4) > endPtr ) + return E_FAIL; + } + + for( ; j > 0; --j, ++x ) + { + if ( x >= image->width ) + return E_FAIL; + + if ( convFlags & CONV_FLAGS_EXPAND ) + { + assert( offset*3 < rowPitch); + + if ( sPtr+2 >= endPtr ) + return E_FAIL; + + // BGR -> RGBA + *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000; + sPtr += 3; + + nonzeroa = true; + } + else + { + assert( offset*4 < rowPitch); + + if ( sPtr+3 >= endPtr ) + return E_FAIL; + + // BGRA -> RGBA + *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 ); + + if ( *(sPtr+3) > 0 ) + nonzeroa = true; + + sPtr += 4; + } + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + } + } + + // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque + if ( !nonzeroa ) + { + HRESULT hr = _SetAlphaChannelToOpaque( image ); + if ( FAILED(hr) ) + return hr; + } + } + break; + + //--------------------------------------------------------------------------------- + default: + return E_FAIL; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Copies pixel data from a TGA into the target image +//------------------------------------------------------------------------------------- +static HRESULT _CopyPixels( _In_bytecount_(size) LPCVOID pSource, size_t size, _In_ const Image* image, DWORD convFlags ) +{ + assert( pSource && size > 0 ); + + if ( !image || !image->pixels ) + return E_POINTER; + + // Compute TGA image data pitch + size_t rowPitch; + if ( convFlags & CONV_FLAGS_EXPAND ) + { + rowPitch = image->width * 3; + } + else + { + size_t slicePitch; + ComputePitch( image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE ); + } + + const uint8_t* sPtr = reinterpret_cast( pSource ); + const uint8_t* endPtr = sPtr + size; + + switch( image->format ) + { + //--------------------------------------------------------------------------- 8-bit + case DXGI_FORMAT_R8_UNORM: + for( size_t y=0; y < image->height; ++y ) + { + size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 ); + assert( offset < rowPitch); + + uint8_t* dPtr = reinterpret_cast( image->pixels ) + + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) + + offset; + + for( size_t x=0; x < image->width; ++x ) + { + if ( sPtr >= endPtr ) + return E_FAIL; + + *dPtr = *(sPtr++); + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + break; + + //-------------------------------------------------------------------------- 16-bit + case DXGI_FORMAT_B5G5R5A1_UNORM: + { + bool nonzeroa = false; + for( size_t y=0; y < image->height; ++y ) + { + size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 ); + assert( offset*2 < rowPitch); + + uint16_t* dPtr = reinterpret_cast( reinterpret_cast( image->pixels ) + + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) ) + + offset; + + for( size_t x=0; x < image->width; ++x ) + { + if ( sPtr+1 >= endPtr ) + return E_FAIL; + + uint16_t t = *sPtr | (*(sPtr+1) << 8); + sPtr += 2; + *dPtr = t; + + if ( t & 0x8000 ) + nonzeroa = true; + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + + // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque + if ( !nonzeroa ) + { + HRESULT hr = _SetAlphaChannelToOpaque( image ); + if ( FAILED(hr) ) + return hr; + } + } + break; + + //----------------------------------------------------------------------- 24/32-bit + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + bool nonzeroa = false; + for( size_t y=0; y < image->height; ++y ) + { + size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 ); + + uint32_t* dPtr = reinterpret_cast( reinterpret_cast( image->pixels ) + + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) ) + + offset; + + for( size_t x=0; x < image->width; ++x ) + { + if ( convFlags & CONV_FLAGS_EXPAND ) + { + assert( offset*3 < rowPitch); + + if ( sPtr+2 >= endPtr ) + return E_FAIL; + + // BGR -> RGBA + *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000; + sPtr += 3; + + nonzeroa = true; + } + else + { + assert( offset*4 < rowPitch); + + if ( sPtr+3 >= endPtr ) + return E_FAIL; + + // BGRA -> RGBA + *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 ); + + if ( *(sPtr+3) > 0 ) + nonzeroa = true; + + sPtr += 4; + } + + if ( convFlags & CONV_FLAGS_INVERTX ) + --dPtr; + else + ++dPtr; + } + } + + // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque + if ( !nonzeroa ) + { + HRESULT hr = _SetAlphaChannelToOpaque( image ); + if ( FAILED(hr) ) + return hr; + } + } + break; + + //--------------------------------------------------------------------------------- + default: + return E_FAIL; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Encodes TGA file header +//------------------------------------------------------------------------------------- +static HRESULT _EncodeTGAHeader( _In_ const Image& image, _Out_ TGA_HEADER& header, DWORD& convFlags ) +{ + assert( IsValid( image.format ) && !IsVideo( image.format ) ); + + memset( &header, 0, sizeof(TGA_HEADER) ); + + if ( (image.width > 0xFFFF) + || (image.height > 0xFFFF) ) + { + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + header.wWidth = static_cast( image.width ); + header.wHeight = static_cast( image.height ); + + switch( image.format ) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + header.bImageType = TGA_TRUECOLOR; + header.bBitsPerPixel = 32; + header.bDescriptor = TGA_FLAGS_INVERTY | 8; + convFlags |= CONV_FLAGS_SWIZZLE; + break; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + header.bImageType = TGA_TRUECOLOR; + header.bBitsPerPixel = 32; + header.bDescriptor = TGA_FLAGS_INVERTY | 8; + break; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + header.bImageType = TGA_TRUECOLOR; + header.bBitsPerPixel = 24; + header.bDescriptor = TGA_FLAGS_INVERTY; + convFlags |= CONV_FLAGS_888; + break; + + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_A8_UNORM: + header.bImageType = TGA_BLACK_AND_WHITE; + header.bBitsPerPixel = 8; + header.bDescriptor = TGA_FLAGS_INVERTY; + break; + + case DXGI_FORMAT_B5G5R5A1_UNORM: + header.bImageType = TGA_TRUECOLOR; + header.bBitsPerPixel = 16; + header.bDescriptor = TGA_FLAGS_INVERTY | 1; + break; + + default: + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Copies BGRX data to form BGR 24bpp data +//------------------------------------------------------------------------------------- +static void _Copy24bppScanline( _Out_bytecap_(outSize) LPVOID pDestination, _In_ size_t outSize, + _In_bytecount_(inSize) LPCVOID pSource, _In_ size_t inSize ) +{ + assert( pDestination && outSize > 0 ); + assert( pSource && inSize > 0 ); + + assert( pDestination != pSource ); + + const uint32_t * __restrict sPtr = reinterpret_cast(pSource); + uint8_t * __restrict dPtr = reinterpret_cast(pDestination); + + const uint8_t* endPtr = dPtr + outSize; + + for( size_t count = 0; count < inSize; count += 4 ) + { + uint32_t t = *(sPtr++); + + if ( dPtr+2 > endPtr ) + return; + + *(dPtr++) = uint8_t(t & 0xFF); // Blue + *(dPtr++) = uint8_t((t & 0xFF00) >> 8); // Green + *(dPtr++) = uint8_t((t & 0xFF0000) >> 16); // Red + } +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Obtain metadata from TGA file in memory/on disk +//------------------------------------------------------------------------------------- +HRESULT GetMetadataFromTGAMemory( LPCVOID pSource, size_t size, TexMetadata& metadata ) +{ + if ( !pSource || size == 0 ) + return E_INVALIDARG; + + size_t offset; + return _DecodeTGAHeader( pSource, size, metadata, offset, 0 ); +} + +HRESULT GetMetadataFromTGAFile( LPCWSTR szFile, TexMetadata& metadata ) +{ + if ( !szFile ) + return E_INVALIDARG; + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) ); +#else + ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) ); +#endif + if ( !hFile ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + // Get the file size + LARGE_INTEGER fileSize = {0}; + +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) + FILE_STANDARD_INFO fileInfo; + if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + fileSize = fileInfo.EndOfFile; +#else + if ( !GetFileSizeEx( hFile.get(), &fileSize ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } +#endif + + // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid TGA file) + if ( fileSize.HighPart > 0 ) + { + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); + } + + // Need at least enough data to fill the standard header to be a valid TGA + if ( fileSize.LowPart < ( sizeof(TGA_HEADER) ) ) + { + return E_FAIL; + } + + // Read the standard header (we don't need the file footer to parse the file) + uint8_t header[sizeof(TGA_HEADER)]; + DWORD bytesRead = 0; + if ( !ReadFile( hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + size_t offset; + return _DecodeTGAHeader( header, bytesRead, metadata, offset, 0 ); +} + + +//------------------------------------------------------------------------------------- +// Load a TGA file in memory +//------------------------------------------------------------------------------------- +HRESULT LoadFromTGAMemory( LPCVOID pSource, size_t size, TexMetadata* metadata, ScratchImage& image ) +{ + if ( !pSource || size == 0 ) + return E_INVALIDARG; + + image.Release(); + + size_t offset; + DWORD convFlags = 0; + TexMetadata mdata; + HRESULT hr = _DecodeTGAHeader( pSource, size, mdata, offset, &convFlags ); + if ( FAILED(hr) ) + return hr; + + if ( offset > size ) + return E_FAIL; + + LPCVOID pPixels = reinterpret_cast( reinterpret_cast(pSource) + offset ); + assert( pPixels ); + + size_t remaining = size - offset; + if ( remaining == 0 ) + return E_FAIL; + + hr = image.Initialize2D( mdata.format, mdata.width, mdata.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + if ( convFlags & CONV_FLAGS_RLE ) + { + hr = _UncompressPixels( pPixels, remaining, image.GetImage(0,0,0), convFlags ); + } + else + { + hr = _CopyPixels( pPixels, remaining, image.GetImage(0,0,0), convFlags ); + } + + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + if ( metadata ) + memcpy( metadata, &mdata, sizeof(TexMetadata) ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Load a TGA file from disk +//------------------------------------------------------------------------------------- +HRESULT LoadFromTGAFile( LPCWSTR szFile, TexMetadata* metadata, ScratchImage& image ) +{ + if ( !szFile ) + return E_INVALIDARG; + + image.Release(); + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) ); +#else + ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) ); +#endif + if ( !hFile ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + // Get the file size + LARGE_INTEGER fileSize = {0}; + +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) + FILE_STANDARD_INFO fileInfo; + if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + fileSize = fileInfo.EndOfFile; +#else + if ( !GetFileSizeEx( hFile.get(), &fileSize ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } +#endif + + // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid TGA file) + if ( fileSize.HighPart > 0 ) + { + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); + } + + // Need at least enough data to fill the header to be a valid TGA + if ( fileSize.LowPart < sizeof(TGA_HEADER) ) + { + return E_FAIL; + } + + // Read the header + uint8_t header[sizeof(TGA_HEADER)]; + DWORD bytesRead = 0; + if ( !ReadFile( hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + size_t offset; + DWORD convFlags = 0; + TexMetadata mdata; + HRESULT hr = _DecodeTGAHeader( header, bytesRead, mdata, offset, &convFlags ); + if ( FAILED(hr) ) + return hr; + + // Read the pixels + DWORD remaining = static_cast( fileSize.LowPart - offset ); + if ( remaining == 0 ) + return E_FAIL; + + if ( offset > sizeof(TGA_HEADER) ) + { + // Skip past the id string + LARGE_INTEGER filePos = { static_cast(offset), 0 }; + if ( !SetFilePointerEx( hFile.get(), filePos, 0, FILE_BEGIN ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + } + + hr = image.Initialize2D( mdata.format, mdata.width, mdata.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + assert( image.GetPixels() ); + + if ( !(convFlags & (CONV_FLAGS_RLE | CONV_FLAGS_EXPAND | CONV_FLAGS_INVERTX)) && (convFlags & CONV_FLAGS_INVERTY) ) + { + // This case we can read directly into the image buffer in place + if ( !ReadFile( hFile.get(), image.GetPixels(), static_cast( image.GetPixelsSize() ), &bytesRead, 0 ) ) + { + image.Release(); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesRead != image.GetPixelsSize() ) + { + image.Release(); + return E_FAIL; + } + + switch( mdata.format ) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + { + // TGA stores 32-bit data in BGRA form, need to swizzle to RGBA + assert( image.GetImageCount() == 1 ); + const Image* img = image.GetImage(0,0,0); + if ( !img ) + return E_POINTER; + + uint8_t *pPixels = img->pixels; + if ( !pPixels ) + return E_POINTER; + + size_t rowPitch = img->rowPitch; + + // Scan for non-zero alpha channel + bool nonzeroa = false; + + for( size_t h = 0; h < img->height; ++h ) + { + const uint32_t* sPtr = reinterpret_cast( pPixels ); + + for( size_t x=0; x < img->width; ++x ) + { + if ( (*sPtr) & 0xff000000 ) + { + nonzeroa = true; + break; + } + + ++sPtr; + } + + if ( nonzeroa ) + break; + + pPixels += rowPitch; + } + + DWORD tflags = ( !nonzeroa ) ? TEXP_SCANLINE_SETALPHA : TEXP_SCANLINE_NONE; + + // Swizzle scanlines + pPixels = img->pixels; + + for( size_t h = 0; h < img->height; ++h ) + { + _SwizzleScanline( pPixels, rowPitch, pPixels, rowPitch, mdata.format, tflags ); + + pPixels += rowPitch; + } + } + break; + + // If we start using DXGI_FORMAT_B8G8R8X8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM we need to check for a fully 0 alpha channel + + case DXGI_FORMAT_B5G5R5A1_UNORM: + { + assert( image.GetImageCount() == 1 ); + const Image* img = image.GetImage(0,0,0); + if ( !img ) + return E_POINTER; + + // Scan for non-zero alpha channel + bool nonzeroa = false; + + const uint8_t *pPixels = img->pixels; + if ( !pPixels ) + return E_POINTER; + + size_t rowPitch = img->rowPitch; + + for( size_t h = 0; h < img->height; ++h ) + { + const uint16_t* sPtr = reinterpret_cast( pPixels ); + + for( size_t x=0; x < img->width; ++x ) + { + if ( *sPtr & 0x8000 ) + { + nonzeroa = true; + break; + } + + ++sPtr; + } + + if ( nonzeroa ) + break; + + pPixels += rowPitch; + } + + // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque + if ( !nonzeroa ) + { + hr = _SetAlphaChannelToOpaque( img ); + if ( FAILED(hr) ) + return hr; + } + } + break; + } + } + else // RLE || EXPAND || INVERTX || !INVERTY + { + std::unique_ptr temp( new uint8_t[ remaining ] ); + if ( !temp ) + { + image.Release(); + return E_OUTOFMEMORY; + } + + if ( !ReadFile( hFile.get(), temp.get(), remaining, &bytesRead, 0 ) ) + { + image.Release(); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesRead != remaining ) + { + image.Release(); + return E_FAIL; + } + + if ( convFlags & CONV_FLAGS_RLE ) + { + hr = _UncompressPixels( temp.get(), remaining, image.GetImage(0,0,0), convFlags ); + } + else + { + hr = _CopyPixels( temp.get(), remaining, image.GetImage(0,0,0), convFlags ); + } + + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + } + + if ( metadata ) + memcpy( metadata, &mdata, sizeof(TexMetadata) ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Save a TGA file to memory +//------------------------------------------------------------------------------------- +HRESULT SaveToTGAMemory( const Image& image, Blob& blob ) +{ + if ( !image.pixels ) + return E_POINTER; + + TGA_HEADER tga_header; + DWORD convFlags = 0; + HRESULT hr = _EncodeTGAHeader( image, tga_header, convFlags ); + if ( FAILED(hr) ) + return hr; + + blob.Release(); + + // Determine memory required for image data + size_t rowPitch, slicePitch; + if ( convFlags & CONV_FLAGS_888 ) + { + rowPitch = image.width * 3; + slicePitch = image.height * rowPitch; + } + else + { + ComputePitch( image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE ); + } + + hr = blob.Initialize( sizeof(TGA_HEADER) + slicePitch ); + if ( FAILED(hr) ) + return hr; + + // Copy header + uint8_t* dPtr = reinterpret_cast( blob.GetBufferPointer() ); + assert( dPtr != 0 ); + memcpy_s( dPtr, blob.GetBufferSize(), &tga_header, sizeof(TGA_HEADER) ); + dPtr += sizeof(TGA_HEADER); + + const uint8_t* pPixels = reinterpret_cast( image.pixels ); + assert( pPixels ); + + for( size_t y = 0; y < image.height; ++y ) + { + // Copy pixels + if ( convFlags & CONV_FLAGS_888 ) + { + _Copy24bppScanline( dPtr, rowPitch, pPixels, image.rowPitch ); + } + else if ( convFlags & CONV_FLAGS_SWIZZLE ) + { + _SwizzleScanline( dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE ); + } + else + { + _CopyScanline( dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE ); + } + + dPtr += rowPitch; + pPixels += image.rowPitch; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Save a TGA file to disk +//------------------------------------------------------------------------------------- +HRESULT SaveToTGAFile( const Image& image, LPCWSTR szFile ) +{ + if ( !szFile ) + return E_INVALIDARG; + + if ( !image.pixels ) + return E_POINTER; + + TGA_HEADER tga_header; + DWORD convFlags = 0; + HRESULT hr = _EncodeTGAHeader( image, tga_header, convFlags ); + if ( FAILED(hr) ) + return hr; + + // Create file and write header +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, 0 ) ) ); +#else + ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 ) ) ); +#endif + if ( !hFile ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + // Determine size for TGA pixel data + size_t rowPitch, slicePitch; + if ( convFlags & CONV_FLAGS_888 ) + { + rowPitch = image.width * 3; + slicePitch = image.height * rowPitch; + } + else + { + ComputePitch( image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE ); + } + + if ( slicePitch < 65535 ) + { + // For small images, it is better to create an in-memory file and write it out + Blob blob; + + hr = SaveToTGAMemory( image, blob ); + if ( FAILED(hr) ) + return hr; + + // Write blob + const DWORD bytesToWrite = static_cast( blob.GetBufferSize() ); + DWORD bytesWritten; + if ( !WriteFile( hFile.get(), blob.GetBufferPointer(), bytesToWrite, + &bytesWritten, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesWritten != bytesToWrite ) + { + return E_FAIL; + } + } + else + { + // Otherwise, write the image one scanline at a time... + std::unique_ptr temp( new uint8_t[ rowPitch ] ); + if ( !temp ) + return E_OUTOFMEMORY; + + // Write header + DWORD bytesWritten; + if ( !WriteFile( hFile.get(), &tga_header, sizeof(TGA_HEADER), &bytesWritten, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesWritten != sizeof(TGA_HEADER) ) + return E_FAIL; + + // Write pixels + const uint8_t* pPixels = reinterpret_cast( image.pixels ); + + for( size_t y = 0; y < image.height; ++y ) + { + // Copy pixels + if ( convFlags & CONV_FLAGS_888 ) + { + _Copy24bppScanline( temp.get(), rowPitch, pPixels, image.rowPitch ); + } + else if ( convFlags & CONV_FLAGS_SWIZZLE ) + { + _SwizzleScanline( temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE ); + } + else + { + _CopyScanline( temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE ); + } + + pPixels += image.rowPitch; + + if ( !WriteFile( hFile.get(), temp.get(), static_cast( rowPitch ), &bytesWritten, 0 ) ) + { + return HRESULT_FROM_WIN32( GetLastError() ); + } + + if ( bytesWritten != rowPitch ) + return E_FAIL; + } + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexUtil.cpp b/DirectXTex/DirectXTexUtil.cpp new file mode 100644 index 0000000..a722353 --- /dev/null +++ b/DirectXTex/DirectXTexUtil.cpp @@ -0,0 +1,525 @@ +//------------------------------------------------------------------------------------- +// DirectXTexUtil.cpp +// +// DirectX Texture Library - Utilities +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +//------------------------------------------------------------------------------------- +// WIC Pixel Format Translation Data +//------------------------------------------------------------------------------------- +struct WICTranslate +{ + GUID wic; + DXGI_FORMAT format; +}; + +static WICTranslate g_WICFormats[] = +{ + { GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT }, + + { GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT }, + { GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM }, + + { GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM }, + { GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM }, // DXGI 1.1 + { GUID_WICPixelFormat32bppBGR, DXGI_FORMAT_B8G8R8X8_UNORM }, // DXGI 1.1 + + { GUID_WICPixelFormat32bppRGBA1010102XR, DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM }, // DXGI 1.1 + { GUID_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM }, + { GUID_WICPixelFormat32bppRGBE, DXGI_FORMAT_R9G9B9E5_SHAREDEXP }, + + { GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM }, + { GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM }, + + { GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT }, + { GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT }, + { GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM }, + { GUID_WICPixelFormat8bppGray, DXGI_FORMAT_R8_UNORM }, + + { GUID_WICPixelFormat8bppAlpha, DXGI_FORMAT_A8_UNORM }, + + { GUID_WICPixelFormatBlackWhite, DXGI_FORMAT_R1_UNORM }, + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + { GUID_WICPixelFormat96bppRGBFloat, DXGI_FORMAT_R32G32B32_FLOAT }, +#endif +}; + +namespace DirectX +{ + +//===================================================================================== +// WIC Utilities +//===================================================================================== + +DXGI_FORMAT _WICToDXGI( const GUID& guid ) +{ + 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 DXGI_FORMAT_UNKNOWN; +} + +bool _DXGIToWIC( DXGI_FORMAT format, GUID& guid ) +{ + for( size_t i=0; i < _countof(g_WICFormats); ++i ) + { + if ( g_WICFormats[i].format == format ) + { + memcpy( &guid, &g_WICFormats[i].wic, sizeof(GUID) ); + return true; + } + } + + // Special cases + switch( format ) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + memcpy( &guid, &GUID_WICPixelFormat32bppRGBA, sizeof(GUID) ); + return true; + + case DXGI_FORMAT_D32_FLOAT: + memcpy( &guid, &GUID_WICPixelFormat32bppGrayFloat, sizeof(GUID) ); + return true; + + case DXGI_FORMAT_D16_UNORM: + memcpy( &guid, &GUID_WICPixelFormat16bppGray, sizeof(GUID) ); + return true; + + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + memcpy( &guid, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID) ); + return true; + + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + memcpy( &guid, &GUID_WICPixelFormat32bppBGR, sizeof(GUID) ); + return true; + } + + memcpy( &guid, &GUID_NULL, sizeof(GUID) ); + return false; +} + +size_t _WICBitsPerPixel( REFGUID targetGuid ) +{ + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return 0; + + ScopedObject cinfo; + if ( FAILED( pWIC->CreateComponentInfo( targetGuid, &cinfo ) ) ) + return 0; + + WICComponentType type; + if ( FAILED( cinfo->GetComponentType( &type ) ) ) + return 0; + + if ( type != WICPixelFormat ) + return 0; + + ScopedObject pfinfo; + if ( FAILED( cinfo->QueryInterface( __uuidof(IWICPixelFormatInfo), reinterpret_cast( &pfinfo ) ) ) ) + return 0; + + UINT bpp; + if ( FAILED( pfinfo->GetBitsPerPixel( &bpp ) ) ) + return 0; + + return bpp; +} + +IWICImagingFactory* _GetWIC() +{ + static IWICImagingFactory* s_Factory = nullptr; + + if ( s_Factory ) + return s_Factory; + + HRESULT hr = CoCreateInstance( + CLSID_WICImagingFactory, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory), + (LPVOID*)&s_Factory + ); + + if ( FAILED(hr) ) + { + s_Factory = nullptr; + return nullptr; + } + + return s_Factory; +} + + +//------------------------------------------------------------------------------------- +// Public helper function to get common WIC codec GUIDs +//------------------------------------------------------------------------------------- +REFGUID GetWICCodec( _In_ WICCodecs codec ) +{ + switch( codec ) + { + case WIC_CODEC_BMP: + return GUID_ContainerFormatBmp; + + case WIC_CODEC_JPEG: + return GUID_ContainerFormatJpeg; + + case WIC_CODEC_PNG: + return GUID_ContainerFormatPng; + + case WIC_CODEC_TIFF: + return GUID_ContainerFormatTiff; + + case WIC_CODEC_GIF: + return GUID_ContainerFormatGif; + + case WIC_CODEC_WMP: + return GUID_ContainerFormatWmp; + + case WIC_CODEC_ICO: + return GUID_ContainerFormatIco; + + default: + return GUID_NULL; + } +} + + +//===================================================================================== +// DXGI Format Utilities +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Returns bits-per-pixel for a given DXGI format, or 0 on failure +//------------------------------------------------------------------------------------- +size_t BitsPerPixel( DXGI_FORMAT fmt ) +{ + switch( fmt ) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return 32; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + return 16; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + return 8; + + case DXGI_FORMAT_R1_UNORM: + return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return 8; + +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: + return 16; + + // We don't support the video formats ( see IsVideo function ) + +#endif // DXGI_1_2_FORMATS + + default: + return 0; + } +} + + +//------------------------------------------------------------------------------------- +// Computes the image row pitch in bytes, and the slice ptich (size in bytes of the image) +// based on DXGI format, width, and height +//------------------------------------------------------------------------------------- +void ComputePitch( DXGI_FORMAT fmt, size_t width, size_t height, + size_t& rowPitch, size_t& slicePitch, DWORD flags ) +{ + assert( IsValid(fmt) && !IsVideo(fmt) ); + + if ( IsCompressed(fmt) ) + { + size_t bpb = ( fmt == DXGI_FORMAT_BC1_TYPELESS + || fmt == DXGI_FORMAT_BC1_UNORM + || fmt == DXGI_FORMAT_BC1_UNORM_SRGB + || fmt == DXGI_FORMAT_BC4_TYPELESS + || fmt == DXGI_FORMAT_BC4_UNORM + || fmt == DXGI_FORMAT_BC4_SNORM) ? 8 : 16; + size_t nbw = std::max( 1, (width + 3) / 4 ); + size_t nbh = std::max( 1, (height + 3) / 4 ); + rowPitch = nbw * bpb; + + slicePitch = rowPitch * nbh; + } + else if ( IsPacked(fmt) ) + { + rowPitch = ( ( width + 1 ) >> 1) * 4; + + slicePitch = rowPitch * height; + } + else + { + size_t bpp; + + if ( flags & CP_FLAGS_24BPP ) + bpp = 24; + else if ( flags & CP_FLAGS_16BPP ) + bpp = 16; + else if ( flags & CP_FLAGS_8BPP ) + bpp = 8; + else + bpp = BitsPerPixel( fmt ); + + if ( flags & CP_FLAGS_LEGACY_DWORD ) + { + // Special computation for some incorrectly created DDS files based on + // legacy DirectDraw assumptions about pitch alignment + rowPitch = ( ( width * bpp + 31 ) / 32 ) * sizeof(uint32_t); + slicePitch = rowPitch * height; + } + else + { + rowPitch = ( width * bpp + 7 ) / 8; + slicePitch = rowPitch * height; + } + } +} + + +//------------------------------------------------------------------------------------- +// Converts to an SRGB equivalent type if available +//------------------------------------------------------------------------------------- +DXGI_FORMAT MakeSRGB( _In_ DXGI_FORMAT fmt ) +{ + switch( fmt ) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + case DXGI_FORMAT_BC1_UNORM: + return DXGI_FORMAT_BC1_UNORM_SRGB; + + case DXGI_FORMAT_BC2_UNORM: + return DXGI_FORMAT_BC2_UNORM_SRGB; + + case DXGI_FORMAT_BC3_UNORM: + return DXGI_FORMAT_BC3_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; + + case DXGI_FORMAT_BC7_UNORM: + return DXGI_FORMAT_BC7_UNORM_SRGB; + + default: + return fmt; + } +} + + +//===================================================================================== +// TexMetadata +//===================================================================================== + +size_t TexMetadata::ComputeIndex( _In_ size_t mip, _In_ size_t item, _In_ size_t slice ) const +{ + if ( mip >= mipLevels ) + return size_t(-1); + + switch( dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + case TEX_DIMENSION_TEXTURE2D: + if ( slice > 0 ) + return size_t(-1); + + if ( item >= arraySize ) + return size_t(-1); + + return (item*( mipLevels ) + mip); + + case TEX_DIMENSION_TEXTURE3D: + if ( item > 0 ) + { + // No support for arrays of volumes + return size_t(-1); + } + else + { + size_t index = 0; + size_t d = depth; + + for( size_t level = 0; level < mip; ++level ) + { + index += d; + if ( d > 1 ) + d >>= 1; + } + + if ( slice >= d ) + return size_t(-1); + + index += slice; + + return index; + } + break; + + default: + return size_t(-1); + } +} + + +//===================================================================================== +// Blob - Bitmap image container +//===================================================================================== + +void Blob::Release() +{ + if ( _buffer ) + { + _aligned_free( _buffer ); + _buffer = nullptr; + } + + _size = 0; +} + +HRESULT Blob::Initialize( size_t size ) +{ + if ( !size ) + return E_INVALIDARG; + + Release(); + + _buffer = _aligned_malloc( size, 16 ); + if ( !_buffer ) + { + Release(); + return E_OUTOFMEMORY; + } + + _size = size; + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTexWIC.cpp b/DirectXTex/DirectXTexWIC.cpp new file mode 100644 index 0000000..c719408 --- /dev/null +++ b/DirectXTex/DirectXTexWIC.cpp @@ -0,0 +1,941 @@ +//------------------------------------------------------------------------------------- +// DirectXTexWIC.cpp +// +// DirectX Texture Library - WIC-based file reader/writer +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//------------------------------------------------------------------------------------- + +#include "directxtexp.h" + +//------------------------------------------------------------------------------------- +// WIC Pixel Format nearest conversion table +//------------------------------------------------------------------------------------- + +struct WICConvert +{ + GUID source; + GUID target; +}; + +static WICConvert g_WICConvert[] = +{ + // Directly support the formats listed in XnaTexUtil::g_WICFormats, so no conversion required + // Note target GUID in this conversion table must be one of those directly supported formats. + + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT + + { GUID_WICPixelFormat16bppBGR555, GUID_WICPixelFormat16bppBGRA5551 }, // DXGI_FORMAT_B5G5R5A1_UNORM + { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM + + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + + { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + + { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT +#endif + + // We don't support n-channel formats +}; + +namespace DirectX +{ + +//------------------------------------------------------------------------------------- +// Returns the DXGI format and optionally the WIC pixel GUID to convert to +//------------------------------------------------------------------------------------- +static DXGI_FORMAT _DetermineFormat( _In_ const WICPixelFormatGUID& pixelFormat, _In_ DWORD flags, + _Out_opt_ WICPixelFormatGUID* pConvert ) +{ + if ( pConvert ) + memset( pConvert, 0, sizeof(WICPixelFormatGUID) ); + + DXGI_FORMAT format = _WICToDXGI( pixelFormat ); + + if ( format == DXGI_FORMAT_UNKNOWN ) + { + for( size_t i=0; i < _countof(g_WICConvert); ++i ) + { + if ( memcmp( &g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 ) + { + if ( pConvert ) + memcpy( pConvert, &g_WICConvert[i].target, sizeof(WICPixelFormatGUID) ); + + format = _WICToDXGI( g_WICConvert[i].target ); + assert( format != DXGI_FORMAT_UNKNOWN ); + break; + } + } + } + + // Handle special cases based on flags + switch (format) + { + case DXGI_FORMAT_B8G8R8A8_UNORM: // BGRA + case DXGI_FORMAT_B8G8R8X8_UNORM: // BGRX + if ( flags & WIC_FLAGS_FORCE_RGB ) + { + format = DXGI_FORMAT_R8G8B8A8_UNORM; + if ( pConvert ) + memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) ); + } + break; + + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + if ( flags & WIC_FLAGS_NO_X2_BIAS ) + { + format = DXGI_FORMAT_R10G10B10A2_UNORM; + if ( pConvert ) + memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA1010102, sizeof(WICPixelFormatGUID) ); + } + break; + + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_B5G6R5_UNORM: + if ( flags & WIC_FLAGS_NO_16BPP ) + { + format = DXGI_FORMAT_R8G8B8A8_UNORM; + if ( pConvert ) + memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) ); + } + break; + + case DXGI_FORMAT_R1_UNORM: + if ( !(flags & WIC_FLAGS_ALLOW_MONO ) ) + { + // By default we want to promote a black & white to gresycale since R1 is not a generally supported D3D format + format = DXGI_FORMAT_R8_UNORM; + if ( pConvert ) + memcpy( pConvert, &GUID_WICPixelFormat8bppGray, sizeof(WICPixelFormatGUID) ); + } + } + + return format; +} + + +//------------------------------------------------------------------------------------- +// Determines metadata for image +//------------------------------------------------------------------------------------- +static HRESULT _DecodeMetadata( _In_ DWORD flags, + _In_ IWICBitmapDecoder *decoder, _In_ IWICBitmapFrameDecode *frame, + _Out_ TexMetadata& metadata, _Out_opt_ WICPixelFormatGUID* pConvert ) +{ + if ( !decoder || !frame ) + return E_POINTER; + + memset( &metadata, 0, sizeof(TexMetadata) ); + metadata.depth = 1; + metadata.mipLevels = 1; + metadata.dimension = TEX_DIMENSION_TEXTURE2D; + + UINT w, h; + HRESULT hr = frame->GetSize( &w, &h ); + if ( FAILED(hr) ) + return hr; + + metadata.width = w; + metadata.height = h; + + if ( flags & WIC_FLAGS_ALL_FRAMES ) + { + UINT fcount; + hr = decoder->GetFrameCount( &fcount ); + if ( FAILED(hr) ) + return hr; + + metadata.arraySize = fcount; + } + else + metadata.arraySize = 1; + + WICPixelFormatGUID pixelFormat; + hr = frame->GetPixelFormat( &pixelFormat ); + if ( FAILED(hr) ) + return hr; + + metadata.format = _DetermineFormat( pixelFormat, flags, pConvert ); + if ( metadata.format == DXGI_FORMAT_UNKNOWN ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Decodes a single frame +//------------------------------------------------------------------------------------- +static HRESULT _DecodeSingleFrame( _In_ DWORD flags, _In_ const TexMetadata& metadata, _In_ const WICPixelFormatGUID& convertGUID, + _In_ IWICBitmapFrameDecode *frame, _Inout_ ScratchImage& image ) +{ + if ( !frame ) + return E_POINTER; + + HRESULT hr = image.Initialize2D( metadata.format, metadata.width, metadata.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *img = image.GetImage( 0, 0, 0 ); + if ( !img ) + return E_POINTER; + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + if ( memcmp( &convertGUID, &GUID_NULL, sizeof(GUID) ) == 0 ) + { + hr = frame->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + else + { + ScopedObject FC; + hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( frame, convertGUID, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Decodes an image array, resizing/format converting as needed +//------------------------------------------------------------------------------------- +static HRESULT _DecodeMultiframe( _In_ DWORD flags, _In_ const TexMetadata& metadata, + _In_ IWICBitmapDecoder *decoder, _Inout_ ScratchImage& image ) +{ + if ( !decoder ) + return E_POINTER; + + HRESULT hr = image.Initialize2D( metadata.format, metadata.width, metadata.height, metadata.arraySize, 1 ); + if ( FAILED(hr) ) + return hr; + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + WICPixelFormatGUID sourceGUID; + if ( !_DXGIToWIC( metadata.format, sourceGUID ) ) + return E_FAIL; + + for( size_t index = 0; index < metadata.arraySize; ++index ) + { + const Image* img = image.GetImage( 0, index, 0 ); + if ( !img ) + return E_POINTER; + + ScopedObject frame; + hr = decoder->GetFrame( static_cast( index ), &frame ); + if ( FAILED(hr) ) + return hr; + + WICPixelFormatGUID pfGuid; + hr = frame->GetPixelFormat( &pfGuid ); + if ( FAILED(hr) ) + return hr; + + UINT w, h; + hr = frame->GetSize( &w, &h ); + if ( FAILED(hr) ) + return hr; + + if ( memcmp( &pfGuid, &sourceGUID, sizeof(WICPixelFormatGUID) ) == 0 ) + { + if ( w == metadata.width && h == metadata.height ) + { + // This frame does not need resized or format converted, just copy... + hr = frame->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + else + { + // This frame needs resizing, but not format converted + ScopedObject scaler; + hr = pWIC->CreateBitmapScaler( &scaler ); + if ( FAILED(hr) ) + return hr; + + hr = scaler->Initialize( frame.Get(), static_cast( metadata.width ), static_cast( metadata.height ), _GetWICInterp( flags ) ); + if ( FAILED(hr) ) + return hr; + + hr = scaler->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + } + else + { + // This frame required format conversion + ScopedObject FC; + hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( frame.Get(), pfGuid, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + if ( w == metadata.width && h == metadata.height ) + { + // This frame is the same size, no need to scale + hr = FC->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + else + { + // This frame needs resizing and format converted + ScopedObject scaler; + hr = pWIC->CreateBitmapScaler( &scaler ); + if ( FAILED(hr) ) + return hr; + + hr = scaler->Initialize( FC.Get(), static_cast( metadata.width ), static_cast( metadata.height ), _GetWICInterp( flags ) ); + if ( FAILED(hr) ) + return hr; + + hr = scaler->CopyPixels( 0, static_cast( img->rowPitch ), static_cast( img->slicePitch ), img->pixels ); + if ( FAILED(hr) ) + return hr; + } + } + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Encodes a single frame +//------------------------------------------------------------------------------------- +static HRESULT _EncodeImage( _In_ const Image& image, _In_ DWORD flags, _In_ IWICBitmapFrameEncode* frame, _In_opt_ IPropertyBag2* props ) +{ + if ( !frame ) + return E_INVALIDARG; + + if ( !image.pixels ) + return E_POINTER; + + WICPixelFormatGUID pfGuid; + if ( !_DXGIToWIC( image.format, pfGuid ) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + HRESULT hr = frame->Initialize( props ); + if ( FAILED(hr) ) + return hr; + +#ifdef _AMD64_ + if ( (image.width > 0xFFFFFFFF) || (image.height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + hr = frame->SetSize( static_cast( image.width ), static_cast( image.height ) ); + if ( FAILED(hr) ) + return hr; + + hr = frame->SetResolution( 72, 72 ); + if ( FAILED(hr) ) + return hr; + + WICPixelFormatGUID targetGuid = pfGuid; + hr = frame->SetPixelFormat( &targetGuid ); + if ( FAILED(hr) ) + return hr; + + if ( memcmp( &targetGuid, &pfGuid, sizeof(WICPixelFormatGUID) ) != 0 ) + { + // Conversion required to write + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject source; + hr = pWIC->CreateBitmapFromMemory( static_cast( image.width ), static_cast( image.height ), pfGuid, + static_cast( image.rowPitch ), static_cast( image.slicePitch ), + image.pixels, &source ); + if ( FAILED(hr) ) + return hr; + + ScopedObject FC; + hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( source.Get(), targetGuid, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + size_t bpp = _WICBitsPerPixel( targetGuid ); + if ( bpp == 0 ) + return E_FAIL; + + size_t rowPitch = ( image.width * bpp + 7 ) / 8; + size_t slicePitch = rowPitch * image.height; + + std::unique_ptr temp( new uint8_t[ slicePitch ] ); + if ( !temp ) + return E_OUTOFMEMORY; + + hr = FC->CopyPixels( 0, static_cast( rowPitch ), static_cast( slicePitch ), temp.get() ); + if ( FAILED(hr) ) + return hr; + + hr = frame->WritePixels( static_cast( image.height ), static_cast( rowPitch ), static_cast( slicePitch ), temp.get() ); + if ( FAILED(hr) ) + return hr; + } + else + { + // No conversion required + hr = frame->WritePixels( static_cast( image.height ), static_cast( image.rowPitch ), static_cast( image.slicePitch ), + reinterpret_cast( image.pixels ) ); + if ( FAILED(hr) ) + return hr; + } + + hr = frame->Commit(); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + +static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags, + _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream ) +{ + if ( !stream ) + return E_INVALIDARG; + + // Initialize WIC + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject encoder; + HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder ); + if ( FAILED(hr) ) + return hr; + + hr = encoder->Initialize( stream, WICBitmapEncoderNoCache ); + if ( FAILED(hr) ) + return hr; + + ScopedObject frame; + ScopedObject props; + hr = encoder->CreateNewFrame( &frame, &props ); + if ( FAILED(hr) ) + return hr; + + if ( memcmp( &guidContainerFormat, &GUID_ContainerFormatBmp, sizeof(WICPixelFormatGUID) ) == 0 ) + { + // Opt-in to the Windows 8 support for writing 32-bit Windows BMP files with an alpha channel if supported + PROPBAG2 option = { 0 }; + option.pstrName = L"EnableV5Header32bppBGRA"; + + VARIANT varValue; + varValue.vt = VT_BOOL; + varValue.boolVal = VARIANT_TRUE; + hr = props->Write( 1, &option, &varValue ); + if ( FAILED(hr) ) + { + // Fails on older versions of WIC, so we default to the null property bag + props.Reset(); + } + } + + hr = _EncodeImage( image, flags, frame.Get(), props.Get() ); + if ( FAILED(hr) ) + return hr; + + hr = encoder->Commit(); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Encodes an image array +//------------------------------------------------------------------------------------- +static HRESULT _EncodeMultiframe( _In_count_(nimages) const Image* images, _In_ size_t nimages, _In_ DWORD flags, + _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream ) +{ + if ( !stream || nimages < 2 ) + return E_INVALIDARG; + + if ( !images ) + return E_POINTER; + + // Initialize WIC + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject encoder; + HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder ); + if ( FAILED(hr) ) + return hr; + + ScopedObject einfo; + hr = encoder->GetEncoderInfo( &einfo ); + if ( FAILED(hr) ) + return hr; + + BOOL mframe = FALSE; + hr = einfo->DoesSupportMultiframe( &mframe ); + if ( FAILED(hr) ) + return hr; + + if ( !mframe ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + hr = encoder->Initialize( stream, WICBitmapEncoderNoCache ); + if ( FAILED(hr) ) + return hr; + + for( size_t index=0; index < nimages; ++index ) + { + ScopedObject frame; + hr = encoder->CreateNewFrame( &frame, nullptr ); + if ( FAILED(hr) ) + return hr; + + hr = _EncodeImage( images[index], flags, frame.Get(), nullptr ); + if ( FAILED(hr) ) + return hr; + } + + hr = encoder->Commit(); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Obtain metadata from WIC-supported file in memory +//------------------------------------------------------------------------------------- +HRESULT GetMetadataFromWICMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata& metadata ) +{ + if ( !pSource || size == 0 ) + return E_INVALIDARG; + +#ifdef _AMD64_ + if ( size > 0xFFFFFFFF ) + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); +#endif + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + // Create input stream for memory + ScopedObject stream; + HRESULT hr = pWIC->CreateStream( &stream ); + if ( FAILED(hr) ) + return hr; + + hr = stream->InitializeFromMemory( reinterpret_cast( const_cast( pSource ) ), + static_cast( size ) ); + if ( FAILED(hr) ) + return hr; + + // Initialize WIC + ScopedObject decoder; + hr = pWIC->CreateDecoderFromStream( stream.Get(), 0, WICDecodeMetadataCacheOnDemand, &decoder ); + if ( FAILED(hr) ) + return hr; + + ScopedObject frame; + hr = decoder->GetFrame( 0, &frame ); + if ( FAILED(hr) ) + return hr; + + // Get metadata + hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), metadata, 0 ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Obtain metadata from WIC-supported file on disk +//------------------------------------------------------------------------------------- +HRESULT GetMetadataFromWICFile( LPCWSTR szFile, DWORD flags, TexMetadata& metadata ) +{ + if ( !szFile ) + return E_INVALIDARG; + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + // Initialize WIC + ScopedObject decoder; + HRESULT hr = pWIC->CreateDecoderFromFilename( szFile, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder ); + if ( FAILED(hr) ) + return hr; + + ScopedObject frame; + hr = decoder->GetFrame( 0, &frame ); + if ( FAILED(hr) ) + return hr; + + // Get metadata + hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), metadata, 0 ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Load a WIC-supported file in memory +//------------------------------------------------------------------------------------- +HRESULT LoadFromWICMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata* metadata, ScratchImage& image ) +{ + if ( !pSource || size == 0 ) + return E_INVALIDARG; + +#ifdef _AMD64_ + if ( size > 0xFFFFFFFF ) + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); +#endif + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + image.Release(); + + // Create input stream for memory + ScopedObject stream; + HRESULT hr = pWIC->CreateStream( &stream ); + if ( FAILED(hr) ) + return hr; + + hr = stream->InitializeFromMemory( reinterpret_cast( const_cast( pSource ) ), static_cast( size ) ); + if ( FAILED(hr) ) + return hr; + + // Initialize WIC + ScopedObject decoder; + hr = pWIC->CreateDecoderFromStream( stream.Get(), 0, WICDecodeMetadataCacheOnDemand, &decoder ); + if ( FAILED(hr) ) + return hr; + + ScopedObject frame; + hr = decoder->GetFrame( 0, &frame ); + if ( FAILED(hr) ) + return hr; + + // Get metadata + TexMetadata mdata; + WICPixelFormatGUID convertGUID = {0}; + hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), mdata, &convertGUID ); + if ( FAILED(hr) ) + return hr; + + if ( (mdata.arraySize > 1) && (flags & WIC_FLAGS_ALL_FRAMES) ) + { + hr = _DecodeMultiframe( flags, mdata, decoder.Get(), image ); + } + else + { + hr = _DecodeSingleFrame( flags, mdata, convertGUID, frame.Get(), image ); + } + + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + if ( metadata ) + memcpy( metadata, &mdata, sizeof(TexMetadata) ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Load a WIC-supported file from disk +//------------------------------------------------------------------------------------- +HRESULT LoadFromWICFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, ScratchImage& image ) +{ + if ( !szFile ) + return E_INVALIDARG; + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + image.Release(); + + // Initialize WIC + ScopedObject decoder; + HRESULT hr = pWIC->CreateDecoderFromFilename( szFile, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder ); + if ( FAILED(hr) ) + return hr; + + ScopedObject frame; + hr = decoder->GetFrame( 0, &frame ); + if ( FAILED(hr) ) + return hr; + + // Get metadata + TexMetadata mdata; + WICPixelFormatGUID convertGUID = {0}; + hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), mdata, &convertGUID ); + if ( FAILED(hr) ) + return hr; + + if ( (mdata.arraySize > 1) && (flags & WIC_FLAGS_ALL_FRAMES) ) + { + hr = _DecodeMultiframe( flags, mdata, decoder.Get(), image ); + } + else + { + hr = _DecodeSingleFrame( flags, mdata, convertGUID, frame.Get(), image ); + } + + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + if ( metadata ) + memcpy( metadata, &mdata, sizeof(TexMetadata) ); + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Save a WIC-supported file to memory +//------------------------------------------------------------------------------------- +HRESULT SaveToWICMemory( const Image& image, DWORD flags, REFGUID guidContainerFormat, Blob& blob ) +{ + if ( !image.pixels ) + return E_POINTER; + + blob.Release(); + + ScopedObject stream; + HRESULT hr = CreateStreamOnHGlobal( 0, TRUE, &stream ); + if ( FAILED(hr) ) + return hr; + + hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get() ); + if ( FAILED(hr) ) + return hr; + + // Copy stream data into blob + STATSTG stat; + hr = stream->Stat( &stat, STATFLAG_NONAME ); + if ( FAILED(hr) ) + return hr; + + if ( stat.cbSize.HighPart > 0 ) + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); + + hr = blob.Initialize( stat.cbSize.LowPart ); + if ( FAILED(hr) ) + return hr; + + LARGE_INTEGER li = { 0 }; + hr = stream->Seek( li, STREAM_SEEK_SET, 0 ); + if ( FAILED(hr) ) + return hr; + + DWORD bytesRead; + hr = stream->Read( blob.GetBufferPointer(), static_cast( blob.GetBufferSize() ), &bytesRead ); + if ( FAILED(hr) ) + return hr; + + if ( bytesRead != blob.GetBufferSize() ) + return E_FAIL; + + return S_OK; +} + +HRESULT SaveToWICMemory( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, Blob& blob ) +{ + if ( !images || nimages == 0 ) + return E_INVALIDARG; + + blob.Release(); + + ScopedObject stream; + HRESULT hr = CreateStreamOnHGlobal( 0, TRUE, &stream ); + if ( FAILED(hr) ) + return hr; + + if ( nimages > 1 ) + hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get() ); + else + hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get() ); + + if ( FAILED(hr) ) + return hr; + + // Copy stream data into blob + STATSTG stat; + hr = stream->Stat( &stat, STATFLAG_NONAME ); + if ( FAILED(hr) ) + return hr; + + if ( stat.cbSize.HighPart > 0 ) + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); + + hr = blob.Initialize( stat.cbSize.LowPart ); + if ( FAILED(hr) ) + return hr; + + LARGE_INTEGER li = { 0 }; + hr = stream->Seek( li, STREAM_SEEK_SET, 0 ); + if ( FAILED(hr) ) + return hr; + + DWORD bytesRead; + hr = stream->Read( blob.GetBufferPointer(), static_cast( blob.GetBufferSize() ), &bytesRead ); + if ( FAILED(hr) ) + return hr; + + if ( bytesRead != blob.GetBufferSize() ) + return E_FAIL; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Save a WIC-supported file to disk +//------------------------------------------------------------------------------------- +HRESULT SaveToWICFile( const Image& image, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile ) +{ + if ( !szFile ) + return E_INVALIDARG; + + if ( !image.pixels ) + return E_POINTER; + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject stream; + HRESULT hr = pWIC->CreateStream( &stream ); + if ( FAILED(hr) ) + return hr; + + hr = stream->InitializeFromFilename( szFile, GENERIC_WRITE ); + if ( FAILED(hr) ) + return hr; + + hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get() ); + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + +HRESULT SaveToWICFile( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile ) +{ + if ( !szFile || !images || nimages == 0 ) + return E_INVALIDARG; + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject stream; + HRESULT hr = pWIC->CreateStream( &stream ); + if ( FAILED(hr) ) + return hr; + + hr = stream->InitializeFromFilename( szFile, GENERIC_WRITE ); + if ( FAILED(hr) ) + return hr; + + if ( nimages > 1 ) + hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get() ); + else + hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get() ); + + if ( FAILED(hr) ) + return hr; + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTex_11_Desktop.sln b/DirectXTex/DirectXTex_11_Desktop.sln new file mode 100644 index 0000000..d2057e8 --- /dev/null +++ b/DirectXTex/DirectXTex_11_Desktop.sln @@ -0,0 +1,31 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTex", "DirectXTex_11_Desktop.vcxproj", "{371B9FA9-4C90-4AC6-A123-ACED756D6C77}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Profile|Win32 = Profile|Win32 + Profile|x64 = Profile|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.Build.0 = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.ActiveCfg = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.ActiveCfg = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.Build.0 = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.ActiveCfg = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.Build.0 = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.ActiveCfg = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.Build.0 = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/DirectXTex/DirectXTex_11_Desktop.vcxproj b/DirectXTex/DirectXTex_11_Desktop.vcxproj new file mode 100644 index 0000000..d3bd56b --- /dev/null +++ b/DirectXTex/DirectXTex_11_Desktop.vcxproj @@ -0,0 +1,428 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Profile + Win32 + + + Profile + x64 + + + Release + Win32 + + + Release + x64 + + + + DirectXTex + {371B9FA9-4C90-4AC6-A123-ACED756D6C77} + DirectXTex + Win32Proj + $(VCTargetsPath11) + + + + StaticLibrary + Unicode + v110 + + + StaticLibrary + Unicode + v110 + + + StaticLibrary + true + Unicode + v110 + + + StaticLibrary + true + Unicode + v110 + + + StaticLibrary + true + Unicode + v110 + + + StaticLibrary + true + Unicode + v110 + + + + + + + + + + + + + + + + + + + + + + + + true + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + true + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + + Level4 + Disabled + MultiThreadedDebugDLL + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;DXGI_1_2_FORMATS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + EditAndContinue + EnableFastChecks + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + Windows + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + Disabled + MultiThreadedDebugDLL + true + true + Fast + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;DXGI_1_2_FORMATS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + EnableFastChecks + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + Windows + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;DXGI_1_2_FORMATS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;DXGI_1_2_FORMATS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;DXGI_1_2_FORMATS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;DXGI_1_2_FORMATS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DirectXTex/DirectXTex_11_Desktop.vcxproj.filters b/DirectXTex/DirectXTex_11_Desktop.vcxproj.filters new file mode 100644 index 0000000..d528cc8 --- /dev/null +++ b/DirectXTex/DirectXTex_11_Desktop.vcxproj.filters @@ -0,0 +1,37 @@ + + + + +{8e114980-c1a3-4ada-ad7c-83caadf5daeb} +rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DirectXTex/DirectXTex_2010.sln b/DirectXTex/DirectXTex_2010.sln new file mode 100644 index 0000000..7606656 --- /dev/null +++ b/DirectXTex/DirectXTex_2010.sln @@ -0,0 +1,31 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTex", "DirectXTex_2010.vcxproj", "{371B9FA9-4C90-4AC6-A123-ACED756D6C77}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Profile|Win32 = Profile|Win32 + Profile|x64 = Profile|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.Build.0 = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.ActiveCfg = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.ActiveCfg = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.Build.0 = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.ActiveCfg = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.Build.0 = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.ActiveCfg = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.Build.0 = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/DirectXTex/DirectXTex_2010.vcxproj b/DirectXTex/DirectXTex_2010.vcxproj new file mode 100644 index 0000000..72244ff --- /dev/null +++ b/DirectXTex/DirectXTex_2010.vcxproj @@ -0,0 +1,421 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Profile + Win32 + + + Profile + x64 + + + Release + Win32 + + + Release + x64 + + + + DirectXTex + {371B9FA9-4C90-4AC6-A123-ACED756D6C77} + DirectXTex + Win32Proj + + + + StaticLibrary + Unicode + + + StaticLibrary + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + true + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + true + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + + Level4 + Disabled + MultiThreadedDebugDLL + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + EditAndContinue + EnableFastChecks + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + Windows + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + Disabled + MultiThreadedDebugDLL + true + true + Fast + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + EnableFastChecks + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + Windows + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + true + true + true + Fast + Sync + ..\XNAMath;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_WINDOWS;_LIB;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + Use + DirectXTexP.h + + + %(AdditionalOptions) + %(AdditionalDependencies) + true + Windows + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DirectXTex/DirectXTex_2010.vcxproj.filters b/DirectXTex/DirectXTex_2010.vcxproj.filters new file mode 100644 index 0000000..d528cc8 --- /dev/null +++ b/DirectXTex/DirectXTex_2010.vcxproj.filters @@ -0,0 +1,37 @@ + + + + +{8e114980-c1a3-4ada-ad7c-83caadf5daeb} +rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DirectXTex/scoped.h b/DirectXTex/scoped.h new file mode 100644 index 0000000..976c7a0 --- /dev/null +++ b/DirectXTex/scoped.h @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------------- +// scoped.h +// +// Utility header with helper classes for exception-safe handling of resources +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------------- + +#if defined(_MSC_VER) && (_MSC_VER > 1000) +#pragma once +#endif + +#include +#include +#include + +//--------------------------------------------------------------------------------- +struct aligned_deleter { void operator()(void* p) { _aligned_free(p); } }; + +typedef std::unique_ptr ScopedAlignedArrayFloat; + +#ifdef USE_XNAMATH +typedef std::unique_ptr ScopedAlignedArrayXMVECTOR; +#else +typedef std::unique_ptr ScopedAlignedArrayXMVECTOR; +#endif + +//--------------------------------------------------------------------------------- +struct handle_closer { void operator()(HANDLE h) { assert(h != INVALID_HANDLE_VALUE); if (h) CloseHandle(h); } }; + +typedef public std::unique_ptr ScopedHandle; + +inline HANDLE safe_handle( HANDLE h ) { return (h == INVALID_HANDLE_VALUE) ? 0 : h; } + + +//--------------------------------------------------------------------------------- +template class ScopedObject +{ +public: + explicit ScopedObject( T *p = 0 ) : _pointer(p) {} + ~ScopedObject() + { + if ( _pointer ) + { + _pointer->Release(); + _pointer = nullptr; + } + } + + bool IsNull() const { return (!_pointer); } + + T& operator*() { return *_pointer; } + T* operator->() { return _pointer; } + T** operator&() { return &_pointer; } + + void Reset(T *p = 0) { if ( _pointer ) { _pointer->Release(); } _pointer = p; } + + T* Get() const { return _pointer; } + +private: + ScopedObject(const ScopedObject&); + ScopedObject& operator=(const ScopedObject&); + + T* _pointer; +}; diff --git a/Texconv/Texconv.rc b/Texconv/Texconv.rc new file mode 100644 index 0000000..ea1409e --- /dev/null +++ b/Texconv/Texconv.rc @@ -0,0 +1,76 @@ +// Microsoft Visual C++ generated resource script. +// +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define IDC_STATIC -1 +#include + + + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MAIN_ICON ICON "directx.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define IDC_STATIC -1\r\n" + "#include \r\n" + "\r\n" + "\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Texconv/Texconv_11_Desktop.sln b/Texconv/Texconv_11_Desktop.sln new file mode 100644 index 0000000..fbde6e8 --- /dev/null +++ b/Texconv/Texconv_11_Desktop.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "texconv", "Texconv_11_Desktop.vcxproj", "{C3A65381-8FD3-4F69-B29E-654B4B0ED136}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTex", "..\DirectXTex\DirectXTex_11_Desktop.vcxproj", "{371B9FA9-4C90-4AC6-A123-ACED756D6C77}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Profile|Win32 = Profile|Win32 + Profile|x64 = Profile|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|Win32.ActiveCfg = Debug|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|Win32.Build.0 = Debug|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|x64.ActiveCfg = Debug|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|x64.Build.0 = Debug|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|Win32.ActiveCfg = Profile|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|Win32.Build.0 = Profile|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|x64.ActiveCfg = Profile|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|x64.Build.0 = Profile|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|Win32.ActiveCfg = Release|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|Win32.Build.0 = Release|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|x64.ActiveCfg = Release|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.Build.0 = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.ActiveCfg = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.ActiveCfg = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.Build.0 = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.ActiveCfg = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.Build.0 = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.ActiveCfg = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.Build.0 = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Texconv/Texconv_11_Desktop.vcxproj b/Texconv/Texconv_11_Desktop.vcxproj new file mode 100644 index 0000000..5de3ade --- /dev/null +++ b/Texconv/Texconv_11_Desktop.vcxproj @@ -0,0 +1,389 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Profile + Win32 + + + Profile + x64 + + + Release + Win32 + + + Release + x64 + + + + texconv + {C3A65381-8FD3-4F69-B29E-654B4B0ED136} + texconv + Win32Proj + $(VCTargetsPath11) + + + + Application + Unicode + v110 + + + Application + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + Application + true + Unicode + v110 + + + + + + + + + + + + + + + + + + + + + + + + true + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + true + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + false + true + $(ExecutablePath) + $(IncludePath) + $(LibraryPath) + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + EditAndContinue + EnableFastChecks + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Console + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + EnableFastChecks + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Console + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + + + + + + + {371b9fa9-4c90-4ac6-a123-aced756d6c77} + + + + + + + \ No newline at end of file diff --git a/Texconv/Texconv_11_Desktop.vcxproj.filters b/Texconv/Texconv_11_Desktop.vcxproj.filters new file mode 100644 index 0000000..71d78bd --- /dev/null +++ b/Texconv/Texconv_11_Desktop.vcxproj.filters @@ -0,0 +1,17 @@ + + + + + {8e114980-c1a3-4ada-ad7c-83caadf5daeb} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/Texconv/directx.ico b/Texconv/directx.ico new file mode 100644 index 0000000000000000000000000000000000000000..bc43c1b2085df668dffff26d80adcb0ef73f23a9 GIT binary patch literal 25214 zcmeI5cVLg#_xSHS39q)lv}=A(BYsjRYY` zH7TWNZ8b`*#-_&kz0UJaROjc@&v*UvdpUTaG(n_2(TGm{_PcFOh|~~Ux5GdBRM&l zyS@?YKO>^Qtc#yK@=u5{>Hq1KU{6RG^@qxdybhWe6!5z*iT1>iBLf2i0)G1;F)?^# zV8BFP|K>v)#c5rObN$+f-L4fz{=Pz5qICr&I2w9ZA=o!DF>ZWtf_-8F&HR|vwHA+S zP{nE~FzDAF;&|;H84$z(0_?9pt3dI%xLlFpBZEgyw2$=+&#I6b7nfMqGcq#Jo?s8G z>p4OzD5~12M`UDVKydKL!F9QFDK%(Ci${(KPDs!KRM7WnalYOj`aV1;F?cLL!o&HH zo}QYR=;fsCL!B+pF9>D>DqtX+7fufKQRxlhe0_a0Thhkeyn_+~CQ{y; z@%qw|zKo3Ya1I_BkPwiRKHirPap3Byuh&#)kYKk51iqc_cQV6y=gx#&M~CO@L-E!DDMq{D4+R$L?E-5kdTxfq^}{V z_R1Ab1$!d>rj4h4Uq|aorEn_P6QL)?y&bKD>Y%L(3D6A!c{^J1u&ALu1T$YCvv07zrvnFZ+6(= z!6P&>)SF>b!Oc5So1q~)?>!?cSoAO+&WTntdR!`nNv zb#2Msz{^zdR)W)mg`~cvLSh3i$ZcKETGtOj<9V&ceSNL5>O1SQ2iYm_>+2vv)?4oO zVC(vAY5ksR*Xy~GfAJGJWQn3*@5$vY`SjS>tb2wrQ&nCi8&h8I$gKQ&N0Pbn&r|Ns z+#hqdaeu&FKT?f~#+=o=|F8FCeJ+oD%)E!_J-;Oi|Mae(jjQ$Vf6F~YoBEUflJ)&_ zc%9Yf=kU%NPuBRd#+x;Mp}5lH8GVvzPM@>rmqkwr$#2osBDh1(xKXzm@PDpn(hw@6 zeq|K<#EB}>vtFboCR*Y;vjmT-B^+XuGRWN^>Z^9p8fl4fb;E-bf`WL~8fkG}p5Y@$ zP8^Fw*5^c(l4}y|bt4hD`dOu8Kw?lXmB6Erv$|-*gU0Jy>)DbHBfTQIkcZ>rkhY$Y zYgBZ2ObkdzXj%n$40YxiS;=bE!lBM8h{vOP=SUTm z$VuN!v4jLmNU+3_Cm%yCkv3Arik1$wunc}XJ(c0{{Dt-ziX=%H@1lQ$VViZO@cg#p!Lh#i_pB~&XL-tMg_ozop8jWHRfS|$MhI-21%{>l zcaT+1@5$6Xo7s-GscmZ?+L!jJ_EZ7D4fx;T^R!Gpw8Ns%AkH-?5WY#P8yP${JRm(K zS7cz_$bfV-*cw=Fl%=vlCyw%tgTtyMjIxwcVB#oCQHBR6r8qIyk?Yb^>qf5f3iVA- zAMcsls{#|3T9+A88mBf;%8SH#J@6kn@@)RdfMnP7^blunj}YfI3C0!~Zfxo4##YzU z*a8yuImCGs-<>>?UBiPYANjz4RQhl2&Kywd&#$ArJ_zgwz6R2OFM++l9$*)+6W9(U z0b2n(unC9*Vt^=MEf5ZT28;&+0Y4zrW-|+bQ%+7M8Q2C)=D8QJ-`Uxu06TyfAl}8r zyvO}g_Uxt--@oO)2MEiS&3xzTYPJB$IdYhGl&MQQ@%)YgR`NbIS1$7~Up})vcW(0o z_w+n@%%{9Nls~`OQ?Q_k;{HqV;^rqHjOUXjN|=2mOPZ(d?#9mVI~6LJ!{y4E3mzV3 zFlDb-s$@!m&l68ivxIVa=x=q|vgU4$8pb6Bbz|?*!+1lFd*jC& znKa2%D^kSB=Qvw{)q= z;8~V0Hxe3Z9<5wyRt+C+rh~tPhnut+GmNZXZ!-8T8#kIe>(&`bNHCA$;*4zBVjgbV zWMunxb9U`oBfEAP*|W#UzJ2C0-(>%OGoL;lfBm(&#C#3p2K5%ZzR2Oyfi!PV`}W;|-NU#+iPz-MwqF^E*3z zI6=pp{LTa9OingAKKQ_7A2!T5*QsL)+`erJf^RNpnw_~X#&4FWDR%X$$unh&ae;q| zef5eQ)Svvx1^={H2Xn73fbvX!gC!opTj<&PEGv}x0#1xnhlWzQG-zCsr>W`C#^ zRKVknl{zawuv?;$xzYUAT7V>6n+`q4B|!LsGZI zT)2KuE397mc~%E4{e8+iLX!rtVJO z92b3n4j#>Uv3s8Z18?r4fv9iK{Rj=#g|ArZVAgK_HA?3#S+4EXwA4L26F0?tt0M^P z^kVNpM-$^?Hf-1sv;XwvhfhPlSh~P!ziXo!rE?XmNQI-@_ogHz#2#Y=le=|oosqIB zHYR3cyghN}srwP3%N^yrb!}d~OwL^SOO~tL;lvJbvd5gea^vanff?H<9vesL)T1}& ze(>qCr3>cKftJscD~D_DB1MaLID2&ap5%nDFI>6(^UptQ+z=hJ(Vo2L%bOu{=gj|j z>5>n1g;Ksk-rPA|v$+{&? zzC5{HU2Mjdw`{ErX(?MaMjg`9-yZwn!LZ>229F#)cKoF2vp<|Y%~8H!!Tfo1(}B&H zg2gI-w|5Iv$6`8g?A)dM!w0<7|CJ#DBSy1;W=^4eVDH9-3Kh)n=)ky?EPi&cJ$6GB z7v+Dty?8*MzJ2@k?>}Vhw3#y|QU0};n^Y@Y5R7tWceWYllC_Q|!$wh2RAFXs=u~&_ z)vH(EfJrl^jkn5otW%f@7Di6_%AMG`DK zZ#n=YXq2;1@iVj@4G+X6Y_`Y8z?^p<4QR&Iu2;bLv07eU>)oVI39CZBl7sfxH%5a= zOx)IOyS61J#99?z)5t*g{v!gkxV$#BW0N9Up+vEaU2)OTiu=aIy+_khQ<67Z6@FQS z=xEpLl_8Gu+Ch;bMatGZnq)N|6%}jWd$YsUgJ161rWLMT|0SZOceCz&9pxoxXvYR6 z%a(ih=uVJPLPp1JyIH&@ykS*1cHzp6hr^q;>)y+1USz^+z1zb&2UB;!{fbTO)+05G z6e(KEQDNi0@6X@w(X>;qewpRzpm&dQmCx?jYgO0~vuV$uLN3_~Dl^f;R(taCnvMNB zWi?L+K||ZOuGJy4LQMR&nci z@o2GJE>2X)M}>piQ;&A8*wC+2pI?iMyy>X$ouZVuw|HTegNrj2iq^V%l;S>qZC-f3 zxX9FJ6;k%xESoPk3yvDu3Ky%@wsYCKO6G>DMsYHzl zLx=Y6-L*Ma*RI{T{-(6{GU3gsZ@v~782IwbFAuOTN7sM%?`yB?HD=710R#T7*gyN0 zWe4d!xjd$qk*qiiPUuv9PR@+Ogy5j+Gq%(Jk6Zm3x67i(e=TnH*M2JGH`{jGq>?G7vcl9rvA5; zE1Qe>KM55pnkV>~8TgrdvHKr1Zfw@$M+iRU5u(}Bw5j=tcY<$_fe-m5K3lT-lpQ)4 zd;R*RGyQyrPbyuzT7Ku(l<@@DAKSGvmFeduKH_$KiuKf6gb$;>=uXOSZQk6((zd+x zlKB!}W;;G@EboVd4#v^g&>tk&z=!96o9i{;m3+AJNvE z_`zZ7qfVS?meF?t{p=q+*a&{hWqhrUj8~>lH}~+JBJf{}ft$>oYnJutV*>H7pW=5$ zQvQ(ote<}R%m*<4j0G%?Z0wr%E?-EL;!7fVWt z+5XW-M)1YH9$m z78$c(fid{@X2=j@!zZ^*n`WGnl8iGxq4TwC#u-1>>9fy_%j3t!1z*+$-_MD;a9Og% zxZ=mU;wNXP?`-($E{xZi_&^SDcftS9NgMeXTXxzn@XIeI7wr_`Irr?@rYQBw;Cq(C zH?6=uC;U}|d#xKcj0b&q>o@#fKl=5I*K5gFq@=NpK#fxSNc^XDi&j0WaMR2?*FgAGFtoaK*Ui7iUWPetwbmM=L)6bWvsx4Xc$ddnO_B*u{+d@4Ynm zwYMf8i8_D#$%@ZEaUj{eo?DrtN%rXQAFj~Az)=%I?dy(Tp}=PgtYJ2(TcPlQok{i$ z;pea4f4A?D$=}38hW&8m)}!T%=W7p5Jc}1Bma+?zFydmy!&e7{#;;!+v3B3NYfqQW z<#U_Ho;mXtF1CBmj;(QrF5mcQ@>dZNYop>4zW!;+!nw2bdnK1#`3e_1us3;2%*l&C zo;$W`_1c)Nd%u`I^Mg4v`0nfB?2;=_!J>D*PmEuC{D;%WkEL%o96EWzTT^GL;or@# zqKlKWYw`Sri|yI8F6`LxBO(s|aOK{_E*(4f96JR=erS(IrQEV- z&t3LZ{JJ&aYu3kX+;He(#xEhgTelxPezXu~YgEP6)xFUE^=rachp&sV-W?3DFT$ z-~3LIVn>rVM@O7Jce7=yj!NHG#|?O)*3q35Si3g*Oo6aIX?by{f4bN>vtJ9 zF|bdu6lQ2+?BTLGa^x?1VAuAtwk?`u@|X`T65 zUUd}MmHd4^4l}ssDO~G9jVcXWcggxL0TdYd&VdsJb3;j|TzSgWuG6qZr)Qsq0u$eu zJhGX4sq!8rOO&f#$FKQsK2vgB$fOAa+W9qTP`7d8CT%)&c&@VJ=gZ^Xe0|K&LH)b- z?%n%OK5L==Lx&C-GNgCcKh(8eum|vezwhwZc1jYmN^KO4lWzpnKFNxaui^I!U?DIC zP#Z;K{oX=w;d`4_}BX+_8exF5$-$wu6MxSQ@b!m4o@$GbMh|$;>n;JGW zt!OjV-`_~DUS=n{S%@{ys$JWhL}%a8SS~t0347u??=}AXgt%e?`ahJi5xk#=KG=*s zv61pG(r5Pnci?U9S553~^z3>n-zm>Qp0h=9m!G{hxns>Fqnn`48x$ui7o> z>k`E1e?foAjveL+dRpVcmw7&luGxyNpNqaaMLhE$`nw@^gr$$sJytweb+YPXVxd+% z7@cL%B~~n0PXY!t&7bhpVy8#daop_6Rrf7`5C#)%lR)5(*@ z>6>qiGjU&M^p`7r=Dc;wj}{h1eCpC7%K9le{4F&3qsE8{PT4lGOgY}jn& zu^C*Mll<6170`bk)TxBMkeir(UHWc>j`XLkGGoS=meljbPQc+YJ&9cx!qyu_eET)} z91W0%frN@p|9x#tXh zJ7ngHdX3w4SX05(#T0yZ@SxY<|D;(Juhwg7=F3^;jX?v43=96~(|%Qb)~xZUHahT? z0dsqdnKXNGCHiYpYwU=CH;d<*HEKeQ%8F3S7E_0RZga`=VRLtVT(hQ4x3F?Wik9`P z=y>E`zXo+`S9d%iqZ>Z(cW2-7^6g~To+Cn>-(4Bv{Ah)-En-pW!fIH}*v_zzc`+lz z`K7@j&XbQMyWW0sHvcCJ9>DNbnIZMj1_3tX#=rkFSF>i#T#6Sjo}1PS0VM%9pc0@c zsEep~eRc!%yOzrZI0OGzmlGZ5%AGrR@$A{NS95W3Y3l6k+#ML?ZeO#mHNF+j(n?fs8l@PkchU8PEuyyeQ3^C(fGgnzZ-#Rp9+S8mCQYSp&y zXwcxh?_0IHbftUuJNE|;eDrkWNVz|D?4zH?jk|N?wb!m}89MaDqF%lB4Qkvtx^UjS zvl-J^5bgyu2Fd|ChyNpU5)u;P#Kuf+cX#*lWy+LkHl=v++41Gd?KxAm>J@3&P^2}s zRkv<}Zp1bs{HhV#NkoySP8Ie}1z%OhnSp>W zP#VYq*nt1?r8MU_O|~XYniM5=SATA~a_{dhRqDWG$YljXU%1@|PZNS#@<+opTcfb3na%Fi^tCmRp`XVh`3b6||8(tO} zjdA|gTMpbm_&{U{+ce9UJ8+JS6xpyr@clVX5%&kUv1ZM^mE*=8u2;G8a`mf!NE7P*>zEVQMxPs8#jvB?IKA@!alhJwrvyQ7sCFz^)2nAk1h>u-`>s`-b5y} z1^-fj3-E7UnfN<%?kH!ME?p|FuT*LANe_=}KZE-%PfxiE&)nz!sA^St%DX33s>p*n zb>x1dM)E}0K=Q?7pf`gz1utoZ2}clX=Z!1G4Mijv{sA@IH2D^*G! z7A-0d3Ko>Bg$l`0H#a%Syxf7GIG-V$!EmhOz(B!X70y5iXC6eBv9GASuk*kdo~ES8 zlP|xNr)gF|A=EoPxtQ^&RdA=-YwYL))?LyFhJ+w zP4L(CplkY115J&eaGfBS7~T|fEoL*a~xaF#?q9x*~b zWNs4StskNJBgOx~0TG=K9Ru&quU>sCutNu354xsRE~#w&&6P=emmNEHen`%P_mA1K$pvR;`3Ah-1w7#u&UXmsGo<5{D>9DXtA-4bAK;(ISu#o2 zGw+14vF9Q(ZJKa?Mh+mOrXsT%^zJSG@cUb=gDtG@pLXt)$5~@geuBS)a48D0&rXxdA;C;>}$#@$>s4^J#-QV1Z3uKB&MGTkfBi6ait0JlajK`EU7+B?rfsfu z)vCSnb-8jkkAVA8=>9#p9|!M~;G70v$Ve&0owY}NRXF=2#i)!H5HOY3fA~Qn;F%wh zr;n9~R3B%}0lI;+O|lQ&JjBOG3bt)4PK=kcJkpqUMsi)Xtr)IUq(a^1=&5LHf&MbTenRWEpF_c8L<=j4ZM-UTvom zbU!(|ibq;$2Z!)jTjvkeL%!A6M$0}1<7n~E5JK^CE zU-tKp&Rhp-w_2CVdwYcnE50gIM)pDbeaMwP(4Ao~R9LEc)&GupN-xy{}E z_}-EwWe>F9jjY)U-t&OwKpxh!4VoI{q=60w8E4L)7Xz-Gdz0++TN<$D0DhPV-PXc0 z7kqu?2|D3E<4;FF3`3U`LcZGIhv#XahE`sn6ziZibJ(6XhTXU!A0R_hR;`lj*c^`) zf9$6dbLU)}a?*e6R{0cNaSFO~wousr5m~W9hWPu(LjUgIuYR1_ zSgF#iUC?HGks`7k-eLbwwu5sNFazicAauo<@fmCagDuZKj2PyYGlXK$0V+!iGX`(Q z&;xu%Gq&Z(($pF?BmvsIjGa*!xnxrwWPI!sIo1$36Q6R7NBQ#jadE~b;#{CKzz&~> z{yvS(%^69Hf7Fip{B{9f$ST*h-eq zgZ=c~#EDn)=g&V0)HNnxw$!L`X)AM_z#MaKSrWisaZU#I0Q)lU#D6V4fD#uLzp!{5 za{-;Lb^ZPKmTb)f?iHAWTHI?uXE&fAzq9eqWPy$WyMtsuaZ$Xn0qY=}hCr9)%x^k# za20*WnO%nu!8#TN*5B3O;Jcji3CWZ}Cb3SP`*!HRu|NTdMefIe|7LL82J8a91XLF& z=zOpPDQ^IcfINT=o8RIkbd5z%WV*_e5(*H7e zW*>GDXO@LC%kntOhn_X-S05B#;=$W*KPp3Nsg_u#+oe zyLLck&%zhli;TU5-sW7kJXT*+?IyMT)F)McT77BtN0A**(I3P<-pQi>)`ku5$Kj)_ zN4~9NeX88ib*nNo33z@Eh*%4|#c~+f01N}l1J2l^20pd$SKS7VD%YJe!6kDHYHyU~ z8|R6o9eB+^wtkI{xQ)JkqPkjrX|=u8_UqHfVT0>DC?8>aYb@izm@$t_mMj^J9;>YM z-`uq6N&;V9pD_aynv1TK6b?cX#Y_C25dts9-y|L#x&G^QX5eHAdSVm|Gr2_h+KR1 z)w`ujmwr7*jvVfae?s%--^M|QweaC;*6$kle;qhI&qv8T?*-BU<)80>uYpB?@=!L_ z&#cAgZ6mc$tug3W_-zA}pUNu!Ko9iyczl$##EdR$97f{-%!L(`LGE&P1LGWjROdqd zX>iwZob28EC*o&;=m1o!G4U;1#>XSCBC&tMa^#TJ%mMpLvK~;GqrComKGL-u4|E6e zW82tNAFEzcnXa-$aaX+cx4u(-r7|D+$r*O>1b@z-%MfJh^c_278+`K%aRE_)7ewPT z&{VLs1$ku6f%?+u?>(J59j{QKLLX2^l(R3^y!q?|=59SYK8!W63i~Gvydr?-*Fu&& z+L`&yvW@UTY%0H14=H|%ui~ws&-z~N%nKJBHV<}<%KjpZp$=nEyLl92n9JJV4-Sur z4+!>_V6O-=MOfRMrE$!G#`Rd&ku6%JRI664HK<$mPi&JWUG0o93R`6rJhYNISP2j5 z7}N&UG3Z**^{jGfKH#Oc40eenFI6ugGcDbubXQQjK<#X`w^etk-=Ol9b(>{x6l08V zjIdf|3~kK8Hsl0>(JhR52>md{v0hC5oaJIyOpW>axFIl*&j#U|ion|A}U_%BPbdkY! zG1!vqxjE(oz9I+0vi-X=hT%LKfe*3J9lQH zEU{qIF~qcJu`HhW(^~j%C3f8B@X%-A`Z@3k5CXIW^0Q8D>I10m&*Bxuo5n2A-^%aE zA*aw#$>r}a1!&6^JZ#7kGjpb8E3jwyn=wFPL3+vn=41?WHV0q%AnWlL=Go#Sja6dL z?(p|dCyv~H`0(Kj>{t9ZG-=W?p<~C}QLL9R=74-9`G`4~0?s{v%E;E7ue~PZr~Jw@ zS#*CMcYO!X+t5#i=}+yY4uB_+i}q~DMS~nP*fSPC>AGNT7Xa@njJ*-{Ij1ANv6nwY z9-YRY{iRwp%a6F*u;If+&6_Xggl3H$J9hjbjuP9ZP4q^1V-2>*GUi|!w%kkfSqXXM zij83VSaJsXkvAiqzw=*{|3HzZnDkMsALf zVP1072~pUi?@-yj38uzoY} z-wroycy@k|9wQemT2%4!<;$wVHP-p3`0F^L{QdjJckOzf{5uI_46|uxF!N9sIg*bt zIHMaa|4V&HT@NbL!QBPk()GF;*a@iJs=TA)P~3GKSz}OMssm(aojDC2EZMQ6y>jQ4 z>Covb?iYv=U1Oi*RGm6E*0yRj51y}e=+GgRIqF9ICGJ`C5Z$WPg!pdVe%5(d#a;zD zjq(a(@S^>^IuGb}gYGr>riM70jo3gb^rXrw)p2TXsO_z`>+i>*e5Lk@&QndG6#B9* z{4y1~??vvNCH{8_zWJec?K|;)e#__1o$Gz_0f97S5e8FQO5D8A|7ic0K$n$5!XukPrEYOx(Ue8yb-9K&27i)AMI zF&N$WK7D_|Sk|b_M{aQDTgXM07+@o?3E09r&VyS#l7e1Ig~!vN_cze{2y^&N&6+op zYS&)>S*uoUHgDcsG9x2H*XlFe{|c{v`aY&rs}~r@#*N*&-HpQTUyHtug0EtbQ_;{b znt6#P-V=-7iDS(t09$}0 zQJ3vc_0&Jb`}gJ7`T2EXZG0Y&FR>NdVk=|Wg6+G7dD)8Jl*pL2Vb5+y?{nT=cJq5L z@4iB}rvoWIJ|`2syh5XD)#7N>@Pe#SrL}^tQ+53<+`0ctS^0b1{>gK!+TP8Yb;E9& zy{S!`O&3&A-2AamoMo9rQvnQMB&h zm8{Rt-Opw|XWcXOnQ>Y4z%YEQ&)k(xro2TW$328+=p<*k3;$^ay;D%>$($Ki?p1m1 z1~dcy8_sDd1zm_w=H*@l&=^N~z#|j%x4zSFn4#9$hAgV{_@7;RzQ@hYEr+|idvRhb zo_aQ^Sg~T=3KuTizfhq<0R;;d9GNd)zL9zJ<{iqJtA6Z3bz%?1m)i}KgI(Z z+UPrb%a$#R`;o^zs$RXJYZ^BWO>NU=-`U>1&)p0Ry!CkE#K(`PPJNs)efsSaAt4u% z#*g1WuV25ICZ3)%vSrKm3V5`o?`l9nAd55k`PWxwzfSlQxqJ5PS>?lq4ae-MRcq_D zMvbnD#sS$sA~(ko_tSGr#P#``4%V3;>nsZXA4t<<>?s)Y;$Yx8K1(6pR#wOduY$j zz_9-!#BDBwgt5)JI^w~Z1 z@B{SRL+rPU=)ObX@iY3uItzkrZJo)%_qWbM=~*25wf5(Ae^SpHfQ$XT_YU|}tHz%# zaqgChgVNzQm-fA7%a+{izm%n`? z+1L{B;z_-(YK5F=^)DDf~z-m@HvGnfxmucS#!Ie@|f_JdJ&x``W+a z!CWXmWb&l03rmXY*duG#Zj)Z7%;~QgH@SVn!jatg;Wj-FLEOZZzCA%y7Q(~`Ic#Fsr`0_;YhRc>7|z>EE!yoy`$Jh@ zF&dL#U66Apv}6N)5Ot{k=-n>z_x z{0sUYM&B@put%n3#&_7S*S%@-5u6mK>U_Jx(s^icplAc`y+xKYDijZAV1t$4Z%xR!;c zUGNJQA}bC;$J^lYlz58j3)NYwJL%tp^Sux4_-f?KT74(d-)2B#%rk)gz%yDv$7g3O z7;|p=)V(+DzYtIy`jbm2o$yN*;@hXOM#(dk2hjeh?lb6q8#?Q8NXX-7{Wop;O9DPj zH2ufYSJuAB4t^^=RszZsC=YQ$2U&3$<#COv(kFS9k^|6ve)1lr5i(;kaW?X2g}>P( zPgPE;tX6%&9O!=8QhANMu=a~MBX@K7@LOubDF1C~-TDAI zzY>X`s{3?VIKZ=%4@`C_3VhQ4PBqzp#yoE5`m2P z20CPDoLl#FMhq?L(SI*s6!R2}9*m;>4C1BM-XF4>++>agm0@TmPi+1A z!^uIG4fxw%pl4?y1C-w6Ig2y<`qnzuzLeI=-wH}|#%<%=f)iu2;&$je3m5cGcJL$j zRa#TF8#EcjIgLHEFRHuQ6C-A`ioMcm{DGcq{0*@3_V(@1#^9fl$1UV`Ng?>hN#npO ze|7&*b%4s3tg*A^b#JaD`bzoIk`wT~6?fA;J1q~6g;}Q_%!@BJ(q`-#-FLh0=XZ6C zk57O*HZ9|~ba0HH-^UtD`Uo0zLFX2P53I9AdM-zCP(7$^s%%mD#_wEn=15=q*0rJQ z(~=SJ7Jur{A+MERkQJ`%-;&2J$I%gwp;c2s$EV0*0ru?O>g3Hr&bXg9nD&( z4(+ou50(!2_+tkSstZq_7ILITLJBJ`4C>?Z-7a4 z=)Qpdb^$7{R8IK-&-51kx?q?2Q9qJ)E}-9T5m(&as8N{O)oR;6-?#oo=Gvm0HvM7~ zI$d!HV7@$wt>%OuY-yM+{EY*dijAalY9nB$uSD*8HjBT*Ad$#W{)U3s(LINt-w%vCwN9P5&pLH#rnbHM z7k@SO=kd_FP=1K*-1&4Qd!vhqBh5$lFJlfP;f)yDkH=oKLz^V-JBjn{;oX<$ku+jp zhw&4>@$&j%yO-BIwX1h}k{H>h7jZzu8`=&Ki65>SNWORGT)lUl;oR%{qI&UHvT~ zHVqw2eL$GzED?uf?D7x?D7}MA7T?z38i^zu+a$x05 zM8r=oHETAH`jxWUP#%bF)TrBuk|i_98y0e6h1^gfC)Ya9Lo7kauNCq_g`8QrwRP*= zzW)B&hQ?J)O8@>PlZzBNMGmr%t19HI$^aTAZ&b+t6>@il98)1zRmd+Da&P5)WaO19 zrAw=w;SyKA{9EKP3%RM*UO)MMN&<8rMP(iNxz@Q0 za(pct)bi1gljKAT`Ont8=-hHHuuW8j3YXSUN9{XpL-joQzt7GFkxy*-OyuthInqKd zwskHeW6YS_%3~XRd{W5)mxa_Nr(4K5cGy8`kEnfu%}2hmkQ*%I{tEfeLN2$Q?9$~7 z=PJv`HElYcoNpnw+o8);pQ}y79FSuy +#include +#include + +#include + +#include "directxtex.h" + +using namespace DirectX; + +enum OPTIONS // Note: dwOptions below assumes 32 or less options. +{ + OPT_WIDTH = 1, + OPT_HEIGHT, + OPT_MIPLEVELS, + OPT_FORMAT, + OPT_FILTER, + OPT_SRGBI, + OPT_SRGBO, + OPT_SRGB, + OPT_PREFIX, + OPT_SUFFIX, + OPT_OUTPUTDIR, + OPT_FILETYPE, + OPT_HFLIP, + OPT_VFLIP, + OPT_DDS_DWORD_ALIGN, + OPT_USE_DX10, + OPT_NOLOGO, + OPT_SEPALPHA +}; + +struct SConversion +{ + WCHAR szSrc [MAX_PATH]; + WCHAR szDest[MAX_PATH]; + + SConversion *pNext; +}; + +struct SValue +{ + LPCWSTR pName; + DWORD dwValue; +}; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +SValue g_pOptions[] = +{ + { L"w", OPT_WIDTH }, + { L"h", OPT_HEIGHT }, + { L"m", OPT_MIPLEVELS }, + { L"f", OPT_FORMAT }, + { L"if", OPT_FILTER }, + { L"srgbi", OPT_SRGBI }, + { L"srgbo", OPT_SRGBO }, + { L"srgb", OPT_SRGB }, + { L"px", OPT_PREFIX }, + { L"sx", OPT_SUFFIX }, + { L"o", OPT_OUTPUTDIR }, + { L"ft", OPT_FILETYPE }, + { L"hflip", OPT_HFLIP }, + { L"vflip", OPT_VFLIP }, + { L"dword", OPT_DDS_DWORD_ALIGN }, + { L"dx10", OPT_USE_DX10 }, + { L"nologo", OPT_NOLOGO }, + { L"sepalpha", OPT_SEPALPHA }, + { nullptr, 0 } +}; + +#define DEFFMT(fmt) { L#fmt, DXGI_FORMAT_ ## fmt } + +SValue g_pFormats[] = +{ + // List does not include _TYPELESS or depth/stencil formats + DEFFMT(R32G32B32A32_FLOAT), + DEFFMT(R32G32B32A32_UINT), + DEFFMT(R32G32B32A32_SINT), + DEFFMT(R32G32B32_FLOAT), + DEFFMT(R32G32B32_UINT), + DEFFMT(R32G32B32_SINT), + DEFFMT(R16G16B16A16_FLOAT), + DEFFMT(R16G16B16A16_UNORM), + DEFFMT(R16G16B16A16_UINT), + DEFFMT(R16G16B16A16_SNORM), + DEFFMT(R16G16B16A16_SINT), + DEFFMT(R32G32_FLOAT), + DEFFMT(R32G32_UINT), + DEFFMT(R32G32_SINT), + DEFFMT(R10G10B10A2_UNORM), + DEFFMT(R10G10B10A2_UINT), + DEFFMT(R11G11B10_FLOAT), + DEFFMT(R8G8B8A8_UNORM), + DEFFMT(R8G8B8A8_UNORM_SRGB), + DEFFMT(R8G8B8A8_UINT), + DEFFMT(R8G8B8A8_SNORM), + DEFFMT(R8G8B8A8_SINT), + DEFFMT(R16G16_FLOAT), + DEFFMT(R16G16_UNORM), + DEFFMT(R16G16_UINT), + DEFFMT(R16G16_SNORM), + DEFFMT(R16G16_SINT), + DEFFMT(R32_FLOAT), + DEFFMT(R32_UINT), + DEFFMT(R32_SINT), + DEFFMT(R8G8_UNORM), + DEFFMT(R8G8_UINT), + DEFFMT(R8G8_SNORM), + DEFFMT(R8G8_SINT), + DEFFMT(R16_FLOAT), + DEFFMT(R16_UNORM), + DEFFMT(R16_UINT), + DEFFMT(R16_SNORM), + DEFFMT(R16_SINT), + DEFFMT(R8_UNORM), + DEFFMT(R8_UINT), + DEFFMT(R8_SNORM), + DEFFMT(R8_SINT), + DEFFMT(A8_UNORM), + //DEFFMT(R1_UNORM) + DEFFMT(R9G9B9E5_SHAREDEXP), + DEFFMT(R8G8_B8G8_UNORM), + DEFFMT(G8R8_G8B8_UNORM), + DEFFMT(BC1_UNORM), + DEFFMT(BC1_UNORM_SRGB), + DEFFMT(BC2_UNORM), + DEFFMT(BC2_UNORM_SRGB), + DEFFMT(BC3_UNORM), + DEFFMT(BC3_UNORM_SRGB), + DEFFMT(BC4_UNORM), + DEFFMT(BC4_SNORM), + DEFFMT(BC5_UNORM), + DEFFMT(BC5_SNORM), + DEFFMT(B5G6R5_UNORM), + DEFFMT(B5G5R5A1_UNORM), + + // DXGI 1.1 formats + DEFFMT(B8G8R8A8_UNORM), + DEFFMT(B8G8R8X8_UNORM), + DEFFMT(R10G10B10_XR_BIAS_A2_UNORM), + DEFFMT(B8G8R8A8_UNORM_SRGB), + DEFFMT(B8G8R8X8_UNORM_SRGB), + DEFFMT(BC6H_UF16), + DEFFMT(BC6H_SF16), + DEFFMT(BC7_UNORM), + DEFFMT(BC7_UNORM_SRGB), + +#ifdef DXGI_1_2_FORMATS + // DXGI 1.2 formats + DEFFMT(B4G4R4A4_UNORM), +#endif + + { nullptr, DXGI_FORMAT_UNKNOWN } +}; + +SValue g_pFilters[] = +{ + { L"POINT", TEX_FILTER_POINT }, + { L"LINEAR", TEX_FILTER_LINEAR }, + { L"CUBIC", TEX_FILTER_CUBIC }, + { L"FANT", TEX_FILTER_FANT }, + { L"POINT_DITHER", TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION }, + { L"LINEAR_DITHER", TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION }, + { L"CUBIC_DITHER", TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION }, + { L"FANT_DITHER", TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION }, + { nullptr, TEX_FILTER_DEFAULT } +}; + +#define CODEC_DDS 0xFFFF0001 +#define CODEC_TGA 0xFFFF0002 + +SValue g_pSaveFileTypes[] = // valid formats to write to +{ + { L"BMP", WIC_CODEC_BMP }, + { L"JPG", WIC_CODEC_JPEG }, + { L"JPEG", WIC_CODEC_JPEG }, + { L"PNG", WIC_CODEC_PNG }, + { L"DDS", CODEC_DDS }, + { L"TGA", CODEC_TGA }, + { L"TIF", WIC_CODEC_TIFF }, + { L"TIFF", WIC_CODEC_TIFF }, + { L"WDP", WIC_CODEC_WMP }, + { L"HDP", WIC_CODEC_WMP }, + { nullptr, CODEC_DDS } +}; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#pragma warning( disable : 4616 6211 ) + +inline static bool ispow2(size_t x) +{ + return ((x != 0) && !(x & (x - 1))); +} + +DWORD LookupByName(const WCHAR *pName, const SValue *pArray) +{ + while(pArray->pName) + { + if(!_wcsicmp(pName, pArray->pName)) + return pArray->dwValue; + + pArray++; + } + + return 0; +} + +const WCHAR* LookupByValue(DWORD pValue, const SValue *pArray) +{ + while(pArray->pName) + { + if(pValue == pArray->dwValue) + return pArray->pName; + + pArray++; + } + + return L""; +} + +void PrintFormat(DXGI_FORMAT Format) +{ + for(SValue *pFormat = g_pFormats; pFormat->pName; pFormat++) + { + if((DXGI_FORMAT) pFormat->dwValue == Format) + { + wprintf( pFormat->pName ); + break; + } + } +} + +void PrintInfo( const TexMetadata& info ) +{ + wprintf( L" (%Iux%Iu", info.width, info.height); + + if ( TEX_DIMENSION_TEXTURE3D == info.dimension ) + wprintf( L"x%Iu", info.depth); + + if ( info.mipLevels > 1 ) + wprintf( L",%Iu", info.mipLevels); + + wprintf( L" "); + PrintFormat( info.format ); + + switch ( info.dimension ) + { + case TEX_DIMENSION_TEXTURE1D: + wprintf( (info.arraySize > 1) ? L" 1DArray" : L" 1D" ); + break; + + case TEX_DIMENSION_TEXTURE2D: + if ( info.miscFlags & TEX_MISC_TEXTURECUBE ) + { + wprintf( (info.arraySize > 1) ? L" CubeArray" : L" Cube" ); + } + else + { + wprintf( (info.arraySize > 1) ? L" 2DArray" : L" 2D" ); + } + break; + + case TEX_DIMENSION_TEXTURE3D: + wprintf( L" 3D"); + break; + } + + wprintf( L")"); +} + + +void PrintList(size_t cch, SValue *pValue) +{ + while(pValue->pName) + { + size_t cchName = wcslen(pValue->pName); + + if(cch + cchName + 2>= 80) + { + wprintf( L"\n "); + cch = 6; + } + + wprintf( L"%s ", pValue->pName ); + cch += cchName + 2; + pValue++; + } + + wprintf( L"\n"); +} + + +void PrintLogo() +{ + wprintf( L"Microsoft (R) DirectX 11 Texture Converter (DirectXTex version)\n"); + wprintf( L"Copyright (C) Microsoft Corp. All rights reserved.\n"); + wprintf( L"\n"); +} + + +void PrintUsage() +{ + PrintLogo(); + + wprintf( L"Usage: texconv \n"); + wprintf( L"\n"); + wprintf( L" -w width\n"); + wprintf( L" -h height\n"); + wprintf( L" -d depth\n"); + wprintf( L" -m miplevels\n"); + wprintf( L" -f format\n"); + wprintf( L" -if image filtering\n"); + wprintf( L" -srgb{i|o} sRGB {input, output}\n"); + wprintf( L" -px name prefix\n"); + wprintf( L" -sx name suffix\n"); + wprintf( L" -o output directory\n"); + wprintf( L" -ft output file type\n"); + wprintf( L" -hflip horizonal flip of source image\n"); + wprintf( L" -vflip vertical flip of source image\n"); + wprintf( L" -sepalpha resize/generate mips alpha channel separately from color channels\n"); + wprintf( L" -dword Use DWORD instead of BYTE alignment (DDS input only)\n"); + wprintf( L" -dx10 Force use of 'DX10' extended header (DDS output only)\n"); + wprintf( L" -nologo suppress copyright message\n"); + + wprintf( L"\n"); + wprintf( L" : "); + PrintList(13, g_pFormats); + + wprintf( L"\n"); + wprintf( L" : "); + PrintList(13, g_pFilters); + + wprintf( L"\n"); + wprintf( L" : "); + PrintList(15, g_pSaveFileTypes); +} + + +//-------------------------------------------------------------------------------------- +// Entry-point +//-------------------------------------------------------------------------------------- +int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) +{ + // Parameters and defaults + HRESULT hr; + INT nReturn; + + size_t width = 0; + size_t height = 0; + size_t mipLevels = 0; + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + DWORD dwFilter = TEX_FILTER_DEFAULT; + DWORD dwSRGB = 0; + DWORD dwFilterOpts = 0; + DWORD FileType = CODEC_DDS; + + WCHAR szPrefix [MAX_PATH]; + WCHAR szSuffix [MAX_PATH]; + WCHAR szOutputDir[MAX_PATH]; + + szPrefix[0] = 0; + szSuffix[0] = 0; + szOutputDir[0] = 0; + + // Initialize COM (needed for WIC) + if( FAILED( hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED) ) ) + { + wprintf( L"Failed to initialize COM (%08X)\n", hr); + return 1; + } + + // Process command line + DWORD dwOptions = 0; + SConversion *pConversion = nullptr; + SConversion **ppConversion = &pConversion; + + for(int iArg = 1; iArg < argc; iArg++) + { + PWSTR pArg = argv[iArg]; + + if(('-' == pArg[0]) || ('/' == pArg[0])) + { + pArg++; + PWSTR pValue; + + for(pValue = pArg; *pValue && (':' != *pValue); pValue++); + + if(*pValue) + *pValue++ = 0; + + DWORD dwOption = LookupByName(pArg, g_pOptions); + + if(!dwOption || (dwOptions & (1 << dwOption))) + { + PrintUsage(); + return 1; + } + + dwOptions |= 1 << dwOption; + + if( (OPT_NOLOGO != dwOption) && (OPT_SEPALPHA != dwOption) + && (OPT_SRGB != dwOption) && (OPT_SRGBI != dwOption) && (OPT_SRGBO != dwOption) + && (OPT_HFLIP != dwOption) && (OPT_VFLIP != dwOption) + && (OPT_DDS_DWORD_ALIGN != dwOption) && (OPT_USE_DX10 != dwOption) ) + { + if(!*pValue) + { + if((iArg + 1 >= argc)) + { + PrintUsage(); + return 1; + } + + iArg++; + pValue = argv[iArg]; + } + } + + switch(dwOption) + { + case OPT_WIDTH: + if (swscanf_s(pValue, L"%Iu", &width) != 1) + { + wprintf( L"Invalid value specified with -w (%s)\n", pValue); + wprintf( L"\n"); + PrintUsage(); + return 1; + } + break; + + case OPT_HEIGHT: + if (swscanf_s(pValue, L"%Iu", &height) != 1) + { + wprintf( L"Invalid value specified with -h (%s)\n", pValue); + printf("\n"); + PrintUsage(); + return 1; + } + break; + + case OPT_MIPLEVELS: + if (swscanf_s(pValue, L"%Iu", &mipLevels) != 1) + { + wprintf( L"Invalid value specified with -m (%s)\n", pValue); + wprintf( L"\n"); + PrintUsage(); + return 1; + } + break; + + case OPT_FORMAT: + format = (DXGI_FORMAT) LookupByName(pValue, g_pFormats); + if ( !format ) + { + wprintf( L"Invalid value specified with -f (%s)\n", pValue); + wprintf( L"\n"); + PrintUsage(); + return 1; + } + break; + + case OPT_FILTER: + dwFilter = LookupByName(pValue, g_pFilters); + if ( !dwFilter ) + { + wprintf( L"Invalid value specified with -if (%s)\n", pValue); + wprintf( L"\n"); + PrintUsage(); + return 1; + } + break; + + case OPT_SRGBI: + dwSRGB |= TEX_FILTER_SRGB_IN; + break; + + case OPT_SRGBO: + dwSRGB |= TEX_FILTER_SRGB_OUT; + break; + + case OPT_SRGB: + dwSRGB |= TEX_FILTER_SRGB; + break; + + case OPT_SEPALPHA: + dwFilterOpts |= TEX_FILTER_SEPARATE_ALPHA; + break; + + case OPT_PREFIX: + wcscpy_s(szPrefix, MAX_PATH, pValue); + break; + + case OPT_SUFFIX: + wcscpy_s(szSuffix, MAX_PATH, pValue); + break; + + case OPT_OUTPUTDIR: + wcscpy_s(szOutputDir, MAX_PATH, pValue); + break; + + case OPT_FILETYPE: + FileType = LookupByName(pValue, g_pSaveFileTypes); + if ( !FileType ) + { + wprintf( L"Invalid value specified with -ft (%s)\n", pValue); + wprintf( L"\n"); + PrintUsage(); + return 1; + } + break; + } + } + else + { + SConversion *pConv = new SConversion; + if ( !pConv ) + return 1; + + wcscpy_s(pConv->szSrc, MAX_PATH, pArg); + + pConv->szDest[0] = 0; + pConv->pNext = nullptr; + + *ppConversion = pConv; + ppConversion = &pConv->pNext; + } + } + + if(!pConversion) + { + PrintUsage(); + return 0; + } + + if(~dwOptions & (1 << OPT_NOLOGO)) + PrintLogo(); + + // Work out out filename prefix and suffix + if(szOutputDir[0] && (L'\\' != szOutputDir[wcslen(szOutputDir) - 1])) + wcscat_s( szOutputDir, MAX_PATH, L"\\" ); + + if(szPrefix[0]) + wcscat_s(szOutputDir, MAX_PATH, szPrefix); + + wcscpy_s(szPrefix, MAX_PATH, szOutputDir); + + const WCHAR* fileTypeName = LookupByValue(FileType, g_pSaveFileTypes); + + if (fileTypeName) + { + wcscat_s(szSuffix, MAX_PATH, L"."); + wcscat_s(szSuffix, MAX_PATH, fileTypeName); + } + else + { + wcscat_s(szSuffix, MAX_PATH, L".unknown"); + } + + if (FileType != CODEC_DDS) + { + mipLevels = 1; + } + + // Convert images + bool nonpow2warn = false; + SConversion *pConv; + + for(pConv = pConversion; pConv; pConv = pConv->pNext) + { + // Load source image + if(pConv != pConversion) + wprintf( L"\n"); + + wprintf( L"reading %s", pConv->szSrc ); + fflush(stdout); + + WCHAR ext[_MAX_EXT]; + WCHAR fname[_MAX_FNAME]; + _wsplitpath_s( pConv->szSrc, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT ); + + TexMetadata info; + ScratchImage *image = new ScratchImage; + + if ( !image ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + goto LError; + } + + if ( _wcsicmp( ext, L".dds" ) == 0 ) + { + hr = LoadFromDDSFile( pConv->szSrc, + (dwOptions & (1 << OPT_DDS_DWORD_ALIGN)) ? DDS_FLAGS_LEGACY_DWORD : DDS_FLAGS_NONE, + &info, *image ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED (%x)\n", hr); + delete image; + continue; + } + } + else if ( _wcsicmp( ext, L".tga" ) == 0 ) + { + hr = LoadFromTGAFile( pConv->szSrc, &info, *image ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED (%x)\n", hr); + delete image; + continue; + } + } + else + { + // WIC shares the same filter values for mode and dither + static_assert( WIC_FLAGS_DITHER == TEX_FILTER_DITHER, "WIC_FLAGS_* & TEX_FILTER_* should match" ); + static_assert( WIC_FLAGS_DITHER_DIFFUSION == TEX_FILTER_DITHER_DIFFUSION, "WIC_FLAGS_* & TEX_FILTER_* should match" ); + static_assert( WIC_FLAGS_FILTER_POINT == TEX_FILTER_POINT, "WIC_FLAGS_* & TEX_FILTER_* should match" ); + static_assert( WIC_FLAGS_FILTER_LINEAR == TEX_FILTER_LINEAR, "WIC_FLAGS_* & TEX_FILTER_* should match" ); + static_assert( WIC_FLAGS_FILTER_CUBIC == TEX_FILTER_CUBIC, "WIC_FLAGS_* & TEX_FILTER_* should match" ); + static_assert( WIC_FLAGS_FILTER_FANT == TEX_FILTER_FANT, "WIC_FLAGS_* & TEX_FILTER_* should match" ); + + hr = LoadFromWICFile( pConv->szSrc, dwFilter, &info, *image ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED (%x)\n", hr); + delete image; + continue; + } + } + + PrintInfo( info ); + + size_t twidth = ( !width ) ? info.width : width; + size_t theight = ( !height ) ? info.height : height; + size_t tMips = ( !mipLevels && info.mipLevels > 1 ) ? info.mipLevels : mipLevels; + DXGI_FORMAT tformat = ( format == DXGI_FORMAT_UNKNOWN ) ? info.format : format; + + // Convert texture + wprintf( L" as"); + fflush(stdout); + + // --- Decompress -------------------------------------------------------------- + if ( IsCompressed( info.format ) ) + { + const Image* img = image->GetImage(0,0,0); + assert( img ); + size_t nimg = image->GetImageCount(); + + ScratchImage *timage = new ScratchImage; + if ( !timage ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + delete image; + goto LError; + } + + hr = Decompress( img, nimg, info, DXGI_FORMAT_UNKNOWN /* picks good default */, *timage ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED [decompress] (%x)\n", hr); + delete timage; + delete image; + continue; + } + + const TexMetadata& tinfo = timage->GetMetadata(); + + info.format = tinfo.format; + + assert( info.width == tinfo.width ); + assert( info.height == tinfo.height ); + assert( info.depth == tinfo.depth ); + assert( info.arraySize == tinfo.arraySize ); + assert( info.mipLevels == tinfo.mipLevels ); + assert( info.miscFlags == tinfo.miscFlags ); + assert( info.dimension == tinfo.dimension ); + + delete image; + image = timage; + } + + // --- Flip/Rotate ------------------------------------------------------------- + if ( dwOptions & ( (1 << OPT_HFLIP) | (1 << OPT_VFLIP) ) ) + { + ScratchImage *timage = new ScratchImage; + if ( !timage ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + delete image; + goto LError; + } + + DWORD dwFlags = 0; + + if ( dwOptions & (1 << OPT_HFLIP) ) + dwFlags |= TEX_FR_FLIP_HORIZONTAL; + + if ( dwOptions & (1 << OPT_VFLIP) ) + dwFlags |= TEX_FR_FLIP_VERTICAL; + + assert( dwFlags != 0 ); + + hr = FlipRotate( image->GetImages(), image->GetImageCount(), image->GetMetadata(), dwFlags, *timage ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED [fliprotate] (%x)\n", hr); + delete timage; + delete image; + goto LError; + } + + const TexMetadata& tinfo = timage->GetMetadata(); + + assert( tinfo.width == twidth && tinfo.height == theight ); + + info.width = tinfo.width; + info.height = tinfo.height; + + assert( info.depth == tinfo.depth ); + assert( info.arraySize == tinfo.arraySize ); + assert( info.mipLevels == tinfo.mipLevels ); + assert( info.miscFlags == tinfo.miscFlags ); + assert( info.format == tinfo.format ); + assert( info.dimension == tinfo.dimension ); + + delete image; + image = timage; + } + + // --- Resize ------------------------------------------------------------------ + if ( info.width != twidth || info.height != theight ) + { + ScratchImage *timage = new ScratchImage; + if ( !timage ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + delete image; + goto LError; + } + + hr = Resize( image->GetImages(), image->GetImageCount(), image->GetMetadata(), twidth, theight, dwFilter | dwFilterOpts, *timage ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED [resize] (%x)\n", hr); + delete timage; + delete image; + goto LError; + } + + const TexMetadata& tinfo = timage->GetMetadata(); + + assert( tinfo.width == twidth && tinfo.height == theight && tinfo.mipLevels == 1 ); + info.width = tinfo.width; + info.height = tinfo.height; + info.mipLevels = 1; + + assert( info.depth == tinfo.depth ); + assert( info.arraySize == tinfo.arraySize ); + assert( info.miscFlags == tinfo.miscFlags ); + assert( info.format == tinfo.format ); + assert( info.dimension == tinfo.dimension ); + + delete image; + image = timage; + } + + // --- Convert ----------------------------------------------------------------- + if ( info.format != tformat && !IsCompressed( tformat ) ) + { + ScratchImage *timage = new ScratchImage; + if ( !timage ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + delete image; + goto LError; + } + + hr = Convert( image->GetImages(), image->GetImageCount(), image->GetMetadata(), tformat, dwFilter | dwSRGB, 05.f, *timage ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED [convert] (%x)\n", hr); + delete timage; + delete image; + goto LError; + } + + const TexMetadata& tinfo = timage->GetMetadata(); + + assert( tinfo.format == tformat ); + info.format = tinfo.format; + + assert( info.width == tinfo.width ); + assert( info.height == tinfo.height ); + assert( info.depth == tinfo.depth ); + assert( info.arraySize == tinfo.arraySize ); + assert( info.mipLevels == tinfo.mipLevels ); + assert( info.miscFlags == tinfo.miscFlags ); + assert( info.dimension == tinfo.dimension ); + + delete image; + image = timage; + } + + // --- Generate mips ----------------------------------------------------------- + if ( !ispow2(info.width) || !ispow2(info.height) || !ispow2(info.depth) ) + { + if ( info.dimension == TEX_DIMENSION_TEXTURE3D ) + { + if ( !tMips ) + { + tMips = 1; + } + else + { + wprintf( L" ERROR: Cannot generate mips for non-power-of-2 volume textures\n" ); + delete image; + goto LError; + } + } + else if ( !tMips || info.mipLevels != 1 ) + { + nonpow2warn = true; + } + } + + if ( !tMips || info.mipLevels != tMips ) + { + ScratchImage *timage = new ScratchImage; + if ( !timage ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + delete image; + goto LError; + } + + if ( info.dimension == TEX_DIMENSION_TEXTURE3D ) + { + hr = GenerateMipMaps3D( image->GetImages(), image->GetImageCount(), image->GetMetadata(), dwFilter | dwFilterOpts, tMips, *timage ); + } + else + { + hr = GenerateMipMaps( image->GetImages(), image->GetImageCount(), image->GetMetadata(), dwFilter | dwFilterOpts, tMips, *timage ); + } + if ( FAILED(hr) ) + { + wprintf( L" FAILED [mipmaps] (%x)\n", hr); + delete timage; + delete image; + goto LError; + } + + const TexMetadata& tinfo = timage->GetMetadata(); + info.mipLevels = tinfo.mipLevels; + + assert( info.width == tinfo.width ); + assert( info.height == tinfo.height ); + assert( info.depth == tinfo.depth ); + assert( info.arraySize == tinfo.arraySize ); + assert( info.mipLevels == tinfo.mipLevels ); + assert( info.miscFlags == tinfo.miscFlags ); + assert( info.dimension == tinfo.dimension ); + + delete image; + image = timage; + } + + // --- Compress ---------------------------------------------------------------- + if ( IsCompressed( tformat ) && (FileType == CODEC_DDS) ) + { + const Image* img = image->GetImage(0,0,0); + assert( img ); + size_t nimg = image->GetImageCount(); + + ScratchImage *timage = new ScratchImage; + if ( !timage ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + delete image; + goto LError; + } + + hr = Compress( img, nimg, info, tformat, TEX_COMPRESS_DEFAULT, 0.5f, *timage ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED [compress] (%x)\n", hr); + delete timage; + delete image; + continue; + } + + const TexMetadata& tinfo = timage->GetMetadata(); + + info.format = tinfo.format; + assert( info.width == tinfo.width ); + assert( info.height == tinfo.height ); + assert( info.depth == tinfo.depth ); + assert( info.arraySize == tinfo.arraySize ); + assert( info.mipLevels == tinfo.mipLevels ); + assert( info.miscFlags == tinfo.miscFlags ); + assert( info.dimension == tinfo.dimension ); + + delete image; + image = timage; + } + + // --- Save result ------------------------------------------------------------- + { + const Image* img = image->GetImage(0,0,0); + assert( img ); + size_t nimg = image->GetImageCount(); + + PrintInfo( info ); + wprintf( L"\n"); + + // Figure out dest filename + WCHAR *pchSlash, *pchDot; + + wcscpy_s(pConv->szDest, MAX_PATH, szPrefix); + + pchSlash = wcsrchr(pConv->szSrc, L'\\'); + if(pchSlash != 0) + wcscat_s(pConv->szDest, MAX_PATH, pchSlash + 1); + else + wcscat_s(pConv->szDest, MAX_PATH, pConv->szSrc); + + pchSlash = wcsrchr(pConv->szDest, '\\'); + pchDot = wcsrchr(pConv->szDest, '.'); + + if(pchDot > pchSlash) + *pchDot = 0; + + wcscat_s(pConv->szDest, MAX_PATH, szSuffix); + + // Write texture + wprintf( L"writing %s", pConv->szDest); + fflush(stdout); + + switch( FileType ) + { + case CODEC_DDS: + hr = SaveToDDSFile( img, nimg, info, + (dwOptions & (1 << OPT_USE_DX10) ) ? DDS_FLAGS_FORCE_DX10_EXT : DDS_FLAGS_NONE, + pConv->szDest ); + break; + + case CODEC_TGA: + hr = SaveToTGAFile( img[0], pConv->szDest ); + break; + + default: + hr = SaveToWICFile( img, nimg, WIC_FLAGS_ALL_FRAMES, GetWICCodec( static_cast(FileType) ), pConv->szDest ); + break; + } + + if(FAILED(hr)) + { + wprintf( L" FAILED (%x)\n", hr); + delete image; + continue; + } + wprintf( L"\n"); + } + + delete image; + } + + if ( nonpow2warn ) + wprintf( L"\n WARNING: Not all feature levels support non-power-of-2 textures with mipmaps\n" ); + + nReturn = 0; + + goto LDone; + +LError: + nReturn = 1; + +LDone: + + while(pConversion) + { + pConv = pConversion; + pConversion = pConversion->pNext; + delete pConv; + } + + return nReturn; +} diff --git a/Texconv/texconv_2010.sln b/Texconv/texconv_2010.sln new file mode 100644 index 0000000..c98d5ee --- /dev/null +++ b/Texconv/texconv_2010.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "texconv", "texconv_2010.vcxproj", "{C3A65381-8FD3-4F69-B29E-654B4B0ED136}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DirectXTex", "..\DirectXTex\DirectXTex_2010.vcxproj", "{371B9FA9-4C90-4AC6-A123-ACED756D6C77}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Profile|Win32 = Profile|Win32 + Profile|x64 = Profile|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|Win32.ActiveCfg = Debug|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|Win32.Build.0 = Debug|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|x64.ActiveCfg = Debug|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Debug|x64.Build.0 = Debug|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|Win32.ActiveCfg = Profile|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|Win32.Build.0 = Profile|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|x64.ActiveCfg = Profile|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Profile|x64.Build.0 = Profile|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|Win32.ActiveCfg = Release|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|Win32.Build.0 = Release|Win32 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|x64.ActiveCfg = Release|x64 + {C3A65381-8FD3-4F69-B29E-654B4B0ED136}.Release|x64.Build.0 = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|Win32.Build.0 = Debug|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.ActiveCfg = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Debug|x64.Build.0 = Debug|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.ActiveCfg = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|Win32.Build.0 = Profile|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.ActiveCfg = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Profile|x64.Build.0 = Profile|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.ActiveCfg = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|Win32.Build.0 = Release|Win32 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.ActiveCfg = Release|x64 + {371B9FA9-4C90-4AC6-A123-ACED756D6C77}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Texconv/texconv_2010.vcxproj b/Texconv/texconv_2010.vcxproj new file mode 100644 index 0000000..016c393 --- /dev/null +++ b/Texconv/texconv_2010.vcxproj @@ -0,0 +1,382 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Profile + Win32 + + + Profile + x64 + + + Release + Win32 + + + Release + x64 + + + + texconv + {C3A65381-8FD3-4F69-B29E-654B4B0ED136} + texconv + Win32Proj + + + + Application + Unicode + + + Application + Unicode + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + true + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + true + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x86;$(LibraryPath) + + + false + true + $(DXSDK_DIR)Utilities\bin\x64;$(DXSDK_DIR)Utilities\bin\x86;$(ExecutablePath) + $(DXSDK_DIR)Include;$(IncludePath) + $(DXSDK_DIR)Lib\x64;$(LibraryPath) + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + EditAndContinue + EnableFastChecks + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Console + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + Disabled + MultiThreadedDebugDLL + false + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;_DEBUG;DEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + EnableFastChecks + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + Console + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + StreamingSIMDExtensions2 + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX86 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + Level4 + MaxSpeed + MultiThreadedDLL + false + true + true + Fast + Sync + ..\XNAMath;..\DirectXTex;%(AdditionalIncludeDirectories) + %(AdditionalOptions) + WIN32;NDEBUG;PROFILE;_CONSOLE;D3DXFX_LARGEADDRESS_HANDLE;USE_XNAMATH;%(PreprocessorDefinitions) + + + %(AdditionalOptions) + ole32.lib;windowscodecs.lib;uuid.lib;%(AdditionalDependencies) + true + Console + true + true + true + true + true + MachineX64 + AsInvoker + %(DelayLoadDLLs) + + + false + + + + + + + + + + + + + + + + + + + {371b9fa9-4c90-4ac6-a123-aced756d6c77} + + + + + + + \ No newline at end of file diff --git a/Texconv/texconv_2010.vcxproj.filters b/Texconv/texconv_2010.vcxproj.filters new file mode 100644 index 0000000..71d78bd --- /dev/null +++ b/Texconv/texconv_2010.vcxproj.filters @@ -0,0 +1,17 @@ + + + + + {8e114980-c1a3-4ada-ad7c-83caadf5daeb} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/WICTextureLoader/WICTextureLoader.cpp b/WICTextureLoader/WICTextureLoader.cpp new file mode 100644 index 0000000..1118c77 --- /dev/null +++ b/WICTextureLoader/WICTextureLoader.cpp @@ -0,0 +1,691 @@ +//-------------------------------------------------------------------------------------- +// File: WICTextureLoader.cpp +// +// Function for loading a WIC image and creating a Direct3D 11 runtime texture for it +// (auto-generating mipmaps if possible) +// +// Note: Assumes application has already called CoInitializeEx +// +// Warning: CreateWICTexture* functions are not thread-safe if given a d3dContext instance for +// auto-gen mipmap support. +// +// 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. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// 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 +#include + +#pragma warning(push) +#pragma warning(disable : 4005) +#include +#pragma warning(pop) + +#include + +#include "WICTextureLoader.h" + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) && !defined(DXGI_1_2_FORMATS) +#define DXGI_1_2_FORMATS +#endif + +//--------------------------------------------------------------------------------- +template class ScopedObject +{ +public: + explicit ScopedObject( T *p = 0 ) : _pointer(p) {} + ~ScopedObject() + { + if ( _pointer ) + { + _pointer->Release(); + _pointer = nullptr; + } + } + + bool IsNull() const { return (!_pointer); } + + T& operator*() { return *_pointer; } + T* operator->() { return _pointer; } + T** operator&() { return &_pointer; } + + void Reset(T *p = 0) { if ( _pointer ) { _pointer->Release(); } _pointer = p; } + + T* Get() const { return _pointer; } + +private: + ScopedObject(const ScopedObject&); + ScopedObject& operator=(const ScopedObject&); + + T* _pointer; +}; + +//------------------------------------------------------------------------------------- +// WIC Pixel Format Translation Data +//------------------------------------------------------------------------------------- +struct WICTranslate +{ + GUID wic; + DXGI_FORMAT format; +}; + +static WICTranslate g_WICFormats[] = +{ + { GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT }, + + { GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT }, + { GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM }, + + { GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM }, + { GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM }, // DXGI 1.1 + { GUID_WICPixelFormat32bppBGR, DXGI_FORMAT_B8G8R8X8_UNORM }, // DXGI 1.1 + + { GUID_WICPixelFormat32bppRGBA1010102XR, DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM }, // DXGI 1.1 + { GUID_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM }, + { GUID_WICPixelFormat32bppRGBE, DXGI_FORMAT_R9G9B9E5_SHAREDEXP }, + +#ifdef DXGI_1_2_FORMATS + + { GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM }, + { GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM }, + +#endif // DXGI_1_2_FORMATS + + { GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT }, + { GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT }, + { GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM }, + { GUID_WICPixelFormat8bppGray, DXGI_FORMAT_R8_UNORM }, + + { GUID_WICPixelFormat8bppAlpha, DXGI_FORMAT_A8_UNORM }, + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + { GUID_WICPixelFormat96bppRGBFloat, DXGI_FORMAT_R32G32B32_FLOAT }, +#endif +}; + +//------------------------------------------------------------------------------------- +// WIC Pixel Format nearest conversion table +//------------------------------------------------------------------------------------- + +struct WICConvert +{ + GUID source; + GUID target; +}; + +static WICConvert g_WICConvert[] = +{ + // Note target GUID in this conversion table must be one of those directly supported formats (above). + + { GUID_WICPixelFormatBlackWhite, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT + +#ifdef DXGI_1_2_FORMATS + + { GUID_WICPixelFormat16bppBGR555, GUID_WICPixelFormat16bppBGRA5551 }, // DXGI_FORMAT_B5G5R5A1_UNORM + +#else + + { GUID_WICPixelFormat16bppBGR555, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat16bppBGRA5551, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat16bppBGR565, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + +#endif // DXGI_1_2_FORMATS + + { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM + + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + + { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + + { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + +#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) + { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT +#endif + + // We don't support n-channel formats +}; + +//-------------------------------------------------------------------------------------- +static IWICImagingFactory* _GetWIC() +{ + static IWICImagingFactory* s_Factory = nullptr; + + if ( s_Factory ) + return s_Factory; + + HRESULT hr = CoCreateInstance( + CLSID_WICImagingFactory, + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWICImagingFactory), + (LPVOID*)&s_Factory + ); + + if ( FAILED(hr) ) + { + s_Factory = nullptr; + return nullptr; + } + + return s_Factory; +} + +//--------------------------------------------------------------------------------- +static DXGI_FORMAT _WICToDXGI( const GUID& guid ) +{ + 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 DXGI_FORMAT_UNKNOWN; +} + +//--------------------------------------------------------------------------------- +static size_t _WICBitsPerPixel( REFGUID targetGuid ) +{ + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return 0; + + ScopedObject cinfo; + if ( FAILED( pWIC->CreateComponentInfo( targetGuid, &cinfo ) ) ) + return 0; + + WICComponentType type; + if ( FAILED( cinfo->GetComponentType( &type ) ) ) + return 0; + + if ( type != WICPixelFormat ) + return 0; + + ScopedObject pfinfo; + if ( FAILED( cinfo->QueryInterface( __uuidof(IWICPixelFormatInfo), reinterpret_cast( &pfinfo ) ) ) ) + return 0; + + UINT bpp; + if ( FAILED( pfinfo->GetBitsPerPixel( &bpp ) ) ) + return 0; + + return bpp; +} + +//--------------------------------------------------------------------------------- +static HRESULT CreateTextureFromWIC( _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_ IWICBitmapFrameDecode *frame, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize ) +{ + UINT width, height; + HRESULT hr = frame->GetSize( &width, &height ); + if ( FAILED(hr) ) + return hr; + + assert( width > 0 && height > 0 ); + + if ( !maxsize ) + { + // This is a bit conservative because the hardware could support larger textures than + // the Feature Level defined minimums, but doing it this way is much easier and more + // performant for WIC than the 'fail and retry' model used by DDSTextureLoader + + switch( d3dDevice->GetFeatureLevel() ) + { + case D3D_FEATURE_LEVEL_9_1: + case D3D_FEATURE_LEVEL_9_2: + maxsize = 2048 /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + break; + + case D3D_FEATURE_LEVEL_9_3: + maxsize = 4096 /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + break; + + case D3D_FEATURE_LEVEL_10_0: + case D3D_FEATURE_LEVEL_10_1: + maxsize = 8192 /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/; + break; + + default: + maxsize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + break; + } + } + + assert( maxsize > 0 ); + + UINT twidth, theight; + if ( width > maxsize || height > maxsize ) + { + float ar = static_cast(height) / static_cast(width); + if ( width > height ) + { + twidth = static_cast( maxsize ); + theight = static_cast( static_cast(maxsize) * ar ); + } + else + { + theight = static_cast( maxsize ); + twidth = static_cast( static_cast(maxsize) / ar ); + } + assert( twidth <= maxsize && theight <= maxsize ); + } + else + { + twidth = width; + theight = height; + } + + // Determine format + WICPixelFormatGUID pixelFormat; + hr = frame->GetPixelFormat( &pixelFormat ); + if ( FAILED(hr) ) + return hr; + + WICPixelFormatGUID convertGUID; + memcpy( &convertGUID, &pixelFormat, sizeof(WICPixelFormatGUID) ); + + size_t bpp = 0; + + DXGI_FORMAT format = _WICToDXGI( pixelFormat ); + if ( format == DXGI_FORMAT_UNKNOWN ) + { + for( size_t i=0; i < _countof(g_WICConvert); ++i ) + { + if ( memcmp( &g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 ) + { + memcpy( &convertGUID, &g_WICConvert[i].target, sizeof(WICPixelFormatGUID) ); + + format = _WICToDXGI( g_WICConvert[i].target ); + assert( format != DXGI_FORMAT_UNKNOWN ); + bpp = _WICBitsPerPixel( convertGUID ); + break; + } + } + + if ( format == DXGI_FORMAT_UNKNOWN ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + } + else + { + bpp = _WICBitsPerPixel( pixelFormat ); + } + + if ( !bpp ) + return E_FAIL; + + // Verify our target format is supported by the current device + // (handles WDDM 1.0 or WDDM 1.1 device driver cases as well as DirectX 11.0 Runtime without 16bpp format support) + UINT support = 0; + hr = d3dDevice->CheckFormatSupport( format, &support ); + if ( FAILED(hr) || !(support & D3D11_FORMAT_SUPPORT_TEXTURE2D) ) + { + // Fallback to RGBA 32-bit format which is supported by all devices + memcpy( &convertGUID, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) ); + format = DXGI_FORMAT_R8G8B8A8_UNORM; + bpp = 32; + } + + // Allocate temporary memory for image + size_t rowPitch = ( twidth * bpp + 7 ) / 8; + size_t imageSize = rowPitch * theight; + + std::unique_ptr temp( new uint8_t[ imageSize ] ); + + // Load image data + if ( memcmp( &convertGUID, &pixelFormat, sizeof(GUID) ) == 0 + && twidth == width + && theight == height ) + { + // No format conversion or resize needed + hr = frame->CopyPixels( 0, static_cast( rowPitch ), static_cast( imageSize ), temp.get() ); + if ( FAILED(hr) ) + return hr; + } + else if ( twidth != width || theight != height ) + { + // Resize + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject scaler; + hr = pWIC->CreateBitmapScaler( &scaler ); + 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 = scaler->CopyPixels( 0, static_cast( rowPitch ), static_cast( imageSize ), temp.get() ); + if ( FAILED(hr) ) + return hr; + } + else + { + ScopedObject FC; + hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( scaler.Get(), convertGUID, WICBitmapDitherTypeErrorDiffusion, 0, 0, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( rowPitch ), static_cast( imageSize ), temp.get() ); + if ( FAILED(hr) ) + return hr; + } + } + else + { + // Format conversion but no resize + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + ScopedObject FC; + hr = pWIC->CreateFormatConverter( &FC ); + if ( FAILED(hr) ) + return hr; + + hr = FC->Initialize( frame, convertGUID, WICBitmapDitherTypeErrorDiffusion, 0, 0, WICBitmapPaletteTypeCustom ); + if ( FAILED(hr) ) + return hr; + + hr = FC->CopyPixels( 0, static_cast( rowPitch ), static_cast( imageSize ), temp.get() ); + if ( FAILED(hr) ) + return hr; + } + + // See if format is supported for auto-gen mipmaps (varies by feature level) + bool autogen = false; + if ( d3dContext != 0 && textureView != 0 ) // Must have context and shader-view to auto generate mipmaps + { + UINT fmtSupport = 0; + hr = d3dDevice->CheckFormatSupport( format, &fmtSupport ); + if ( SUCCEEDED(hr) && ( fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN ) ) + { + autogen = true; + } + } + + // Create texture + D3D11_TEXTURE2D_DESC desc; + desc.Width = twidth; + desc.Height = theight; + desc.MipLevels = (autogen) ? 0 : 1; + desc.ArraySize = 1; + desc.Format = format; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = (autogen) ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : (D3D11_BIND_SHADER_RESOURCE); + desc.CPUAccessFlags = 0; + desc.MiscFlags = (autogen) ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0; + + D3D11_SUBRESOURCE_DATA initData; + initData.pSysMem = temp.get(); + initData.SysMemPitch = static_cast( rowPitch ); + initData.SysMemSlicePitch = static_cast( imageSize ); + + ID3D11Texture2D* tex = nullptr; + hr = d3dDevice->CreateTexture2D( &desc, (autogen) ? nullptr : &initData, &tex ); + if ( SUCCEEDED(hr) && tex != 0 ) + { + if (textureView != 0) + { + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + memset( &SRVDesc, 0, sizeof( SRVDesc ) ); + SRVDesc.Format = format; + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + SRVDesc.Texture2D.MipLevels = (autogen) ? -1 : 1; + + hr = d3dDevice->CreateShaderResourceView( tex, &SRVDesc, textureView ); + if ( FAILED(hr) ) + { + tex->Release(); + return hr; + } + + if ( autogen ) + { + assert( d3dContext != 0 ); + d3dContext->UpdateSubresource( tex, 0, nullptr, temp.get(), static_cast(rowPitch), static_cast(imageSize) ); + d3dContext->GenerateMips( *textureView ); + } + } + + if (texture != 0) + { + *texture = tex; + } + else + { +#if defined(DEBUG) || defined(PROFILE) + tex->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("WICTextureLoader")-1, + "WICTextureLoader" + ); +#endif + tex->Release(); + } + } + + return hr; +} + +//-------------------------------------------------------------------------------------- +HRESULT CreateWICTextureFromMemory( _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_bytecount_(wicDataSize) const uint8_t* wicData, + _In_ size_t wicDataSize, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize + ) +{ + if (!d3dDevice || !wicData || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + if ( !wicDataSize ) + { + return E_FAIL; + } + +#ifdef _M_AMD64 + if ( wicDataSize > 0xFFFFFFFF ) + return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE ); +#endif + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + // Create input stream for memory + ScopedObject stream; + HRESULT hr = pWIC->CreateStream( &stream ); + if ( FAILED(hr) ) + return hr; + + hr = stream->InitializeFromMemory( const_cast( wicData ), static_cast( wicDataSize ) ); + if ( FAILED(hr) ) + return hr; + + // Initialize WIC + ScopedObject decoder; + hr = pWIC->CreateDecoderFromStream( stream.Get(), 0, WICDecodeMetadataCacheOnDemand, &decoder ); + if ( FAILED(hr) ) + return hr; + + ScopedObject frame; + hr = decoder->GetFrame( 0, &frame ); + if ( FAILED(hr) ) + return hr; + + hr = CreateTextureFromWIC( d3dDevice, d3dContext, frame.Get(), texture, textureView, maxsize ); + if ( FAILED(hr)) + return hr; + +#if defined(DEBUG) || defined(PROFILE) + if (texture != 0 && *texture != 0) + { + (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("WICTextureLoader")-1, + "WICTextureLoader" + ); + } + + if (textureView != 0 && *textureView != 0) + { + (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, + sizeof("WICTextureLoader")-1, + "WICTextureLoader" + ); + } +#endif + + return hr; +} + +//-------------------------------------------------------------------------------------- +HRESULT CreateWICTextureFromFile( _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_z_ const wchar_t* fileName, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize ) +{ + if (!d3dDevice || !fileName || (!texture && !textureView)) + { + return E_INVALIDARG; + } + + IWICImagingFactory* pWIC = _GetWIC(); + if ( !pWIC ) + return E_NOINTERFACE; + + // Initialize WIC + ScopedObject decoder; + HRESULT hr = pWIC->CreateDecoderFromFilename( fileName, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder ); + if ( FAILED(hr) ) + return hr; + + ScopedObject frame; + hr = decoder->GetFrame( 0, &frame ); + if ( FAILED(hr) ) + return hr; + + hr = CreateTextureFromWIC( d3dDevice, d3dContext, frame.Get(), texture, textureView, maxsize ); + if ( FAILED(hr)) + return hr; + +#if defined(DEBUG) || defined(PROFILE) + if (texture != 0 || textureView != 0) + { + CHAR strFileA[MAX_PATH]; + WideCharToMultiByte( CP_ACP, + WC_NO_BEST_FIT_CHARS, + fileName, + -1, + strFileA, + MAX_PATH, + nullptr, + FALSE + ); + const CHAR* pstrName = strrchr( strFileA, '\\' ); + if (!pstrName) + { + pstrName = strFileA; + } + else + { + pstrName++; + } + + if (texture != 0 && *texture != 0) + { + (*texture)->SetPrivateData( WKPDID_D3DDebugObjectName, + lstrlenA(pstrName), + pstrName + ); + } + + if (textureView != 0 && *textureView != 0 ) + { + (*textureView)->SetPrivateData( WKPDID_D3DDebugObjectName, + lstrlenA(pstrName), + pstrName + ); + } + } +#endif + + return hr; +} diff --git a/WICTextureLoader/WICTextureLoader.h b/WICTextureLoader/WICTextureLoader.h new file mode 100644 index 0000000..2bc1580 --- /dev/null +++ b/WICTextureLoader/WICTextureLoader.h @@ -0,0 +1,54 @@ +//-------------------------------------------------------------------------------------- +// File: WICTextureLoader.h +// +// Function for loading a WIC image and creating a Direct3D 11 runtime texture for it +// (auto-generating mipmaps if possible) +// +// Note: Assumes application has already called CoInitializeEx +// +// Warning: CreateWICTexture* functions are not thread-safe if given a d3dContext instance for +// auto-gen mipmap support. +// +// 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. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +#pragma warning(push) +#pragma warning(disable : 4005) +#include +#pragma warning(pop) + +HRESULT CreateWICTextureFromMemory( _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_bytecount_(wicDataSize) const uint8_t* wicData, + _In_ size_t wicDataSize, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0 + ); + +HRESULT CreateWICTextureFromFile( _In_ ID3D11Device* d3dDevice, + _In_opt_ ID3D11DeviceContext* d3dContext, + _In_z_ const wchar_t* szFileName, + _Out_opt_ ID3D11Resource** texture, + _Out_opt_ ID3D11ShaderResourceView** textureView, + _In_ size_t maxsize = 0 + );