2016-08-24 03:56:55 +03:00
//--------------------------------------------------------------------------------------
// File: ScreenGrab.cpp
//
// Function for capturing a 2D texture and saving it to a file (aka a 'screenshot'
// when used on a Direct3D Render Target).
//
// Note these functions are useful as a light-weight runtime screen grabber. For
// full-featured texture capture, DDS writer, and texture processing pipeline,
// see the 'Texconv' sample and the 'DirectXTex' library.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
2018-02-23 22:49:48 +03:00
// Licensed under the MIT License.
2016-08-24 03:56:55 +03:00
//
// http://go.microsoft.com/fwlink/?LinkId=248926
// http://go.microsoft.com/fwlink/?LinkId=248929
//--------------------------------------------------------------------------------------
// Does not capture 1D textures or 3D textures (volume maps)
// Does not capture mipmap chains, only the top-most texture level is saved
// For 2D array textures and cubemaps, it captures only the first image in the array
# include "pch.h"
# include "ScreenGrab.h"
# include "DirectXHelpers.h"
# include "PlatformHelpers.h"
2019-05-25 00:26:52 +03:00
# include "DDS.h"
2016-08-24 03:56:55 +03:00
# include "LoaderHelpers.h"
using Microsoft : : WRL : : ComPtr ;
using namespace DirectX ;
2016-09-12 22:25:36 +03:00
using namespace DirectX : : LoaderHelpers ;
2016-08-24 03:56:55 +03:00
namespace
{
//--------------------------------------------------------------------------------------
2018-03-16 21:33:06 +03:00
HRESULT CaptureTexture (
_In_ ID3D11DeviceContext * pContext ,
2016-08-24 03:56:55 +03:00
_In_ ID3D11Resource * pSource ,
2016-09-14 22:13:46 +03:00
D3D11_TEXTURE2D_DESC & desc ,
ComPtr < ID3D11Texture2D > & pStaging )
2016-08-24 03:56:55 +03:00
{
if ( ! pContext | | ! pSource )
return E_INVALIDARG ;
D3D11_RESOURCE_DIMENSION resType = D3D11_RESOURCE_DIMENSION_UNKNOWN ;
pSource - > GetType ( & resType ) ;
if ( resType ! = D3D11_RESOURCE_DIMENSION_TEXTURE2D )
2018-07-27 08:08:10 +03:00
{
DebugTrace ( " ERROR: ScreenGrab does not support 1D or volume textures. Consider using DirectXTex instead. \n " ) ;
2016-08-24 03:56:55 +03:00
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
2018-07-27 08:08:10 +03:00
}
2016-08-24 03:56:55 +03:00
ComPtr < ID3D11Texture2D > pTexture ;
HRESULT hr = pSource - > QueryInterface ( IID_GRAPHICS_PPV_ARGS ( pTexture . GetAddressOf ( ) ) ) ;
if ( FAILED ( hr ) )
return hr ;
assert ( pTexture ) ;
pTexture - > GetDesc ( & desc ) ;
2018-07-27 08:08:10 +03:00
if ( desc . ArraySize > 1 | | desc . MipLevels > 1 )
{
DebugTrace ( " WARNING: ScreenGrab does not support 2D arrays, cubemaps, or mipmaps; only the first surface is written. Consider using DirectXTex instead. \n " ) ;
}
2016-08-24 03:56:55 +03:00
ComPtr < ID3D11Device > d3dDevice ;
pContext - > GetDevice ( d3dDevice . GetAddressOf ( ) ) ;
if ( desc . SampleDesc . Count > 1 )
{
// MSAA content must be resolved before being copied to a staging texture
desc . SampleDesc . Count = 1 ;
desc . SampleDesc . Quality = 0 ;
ComPtr < ID3D11Texture2D > pTemp ;
2018-06-13 04:05:42 +03:00
hr = d3dDevice - > CreateTexture2D ( & desc , nullptr , pTemp . GetAddressOf ( ) ) ;
2016-08-24 03:56:55 +03:00
if ( FAILED ( hr ) )
return hr ;
assert ( pTemp ) ;
DXGI_FORMAT fmt = EnsureNotTypeless ( desc . Format ) ;
UINT support = 0 ;
hr = d3dDevice - > CheckFormatSupport ( fmt , & support ) ;
if ( FAILED ( hr ) )
return hr ;
if ( ! ( support & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE ) )
return E_FAIL ;
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 , fmt ) ;
}
}
desc . BindFlags = 0 ;
desc . MiscFlags & = D3D11_RESOURCE_MISC_TEXTURECUBE ;
desc . CPUAccessFlags = D3D11_CPU_ACCESS_READ ;
desc . Usage = D3D11_USAGE_STAGING ;
2018-06-13 04:05:42 +03:00
hr = d3dDevice - > CreateTexture2D ( & desc , nullptr , pStaging . ReleaseAndGetAddressOf ( ) ) ;
2016-08-24 03:56:55 +03:00
if ( FAILED ( hr ) )
return hr ;
assert ( pStaging ) ;
pContext - > CopyResource ( pStaging . Get ( ) , pTemp . Get ( ) ) ;
}
else if ( ( desc . Usage = = D3D11_USAGE_STAGING ) & & ( desc . CPUAccessFlags & D3D11_CPU_ACCESS_READ ) )
{
// Handle case where the source is already a staging texture we can use directly
pStaging = pTexture ;
}
else
{
// Otherwise, create a staging texture from the non-MSAA source
desc . BindFlags = 0 ;
desc . MiscFlags & = D3D11_RESOURCE_MISC_TEXTURECUBE ;
desc . CPUAccessFlags = D3D11_CPU_ACCESS_READ ;
desc . Usage = D3D11_USAGE_STAGING ;
2018-06-13 04:05:42 +03:00
hr = d3dDevice - > CreateTexture2D ( & desc , nullptr , pStaging . ReleaseAndGetAddressOf ( ) ) ;
2016-08-24 03:56:55 +03:00
if ( FAILED ( hr ) )
return hr ;
assert ( pStaging ) ;
pContext - > CopyResource ( pStaging . Get ( ) , pSource ) ;
}
2018-03-16 21:33:06 +03:00
# if defined(_XBOX_ONE) && defined(_TITLE)
2016-08-24 03:56:55 +03:00
if ( d3dDevice - > GetCreationFlags ( ) & D3D11_CREATE_DEVICE_IMMEDIATE_CONTEXT_FAST_SEMANTICS )
{
ComPtr < ID3D11DeviceX > d3dDeviceX ;
hr = d3dDevice . As ( & d3dDeviceX ) ;
if ( FAILED ( hr ) )
return hr ;
ComPtr < ID3D11DeviceContextX > d3dContextX ;
hr = pContext - > QueryInterface ( IID_GRAPHICS_PPV_ARGS ( d3dContextX . GetAddressOf ( ) ) ) ;
if ( FAILED ( hr ) )
return hr ;
UINT64 copyFence = d3dContextX - > InsertFence ( 0 ) ;
while ( d3dDeviceX - > IsFencePending ( copyFence ) )
{
SwitchToThread ( ) ;
}
}
2018-03-16 21:33:06 +03:00
# endif
2016-08-24 03:56:55 +03:00
return S_OK ;
}
} // anonymous namespace
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
2018-03-16 21:33:06 +03:00
HRESULT DirectX : : SaveDDSTextureToFile (
ID3D11DeviceContext * pContext ,
ID3D11Resource * pSource ,
2019-12-11 04:23:15 +03:00
const wchar_t * fileName ) noexcept
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
if ( ! fileName )
2016-08-24 03:56:55 +03:00
return E_INVALIDARG ;
D3D11_TEXTURE2D_DESC desc = { } ;
ComPtr < ID3D11Texture2D > pStaging ;
2018-03-16 21:33:06 +03:00
HRESULT hr = CaptureTexture ( pContext , pSource , desc , pStaging ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
// Create file
# if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
2018-03-16 21:33:06 +03:00
ScopedHandle hFile ( safe_handle ( CreateFile2 ( fileName , GENERIC_WRITE | DELETE , 0 , CREATE_ALWAYS , nullptr ) ) ) ;
2016-08-24 03:56:55 +03:00
# else
2018-03-16 21:33:06 +03:00
ScopedHandle hFile ( safe_handle ( CreateFileW ( fileName , GENERIC_WRITE | DELETE , 0 , nullptr , CREATE_ALWAYS , 0 , nullptr ) ) ) ;
2016-08-24 03:56:55 +03:00
# endif
2018-03-16 21:33:06 +03:00
if ( ! hFile )
return HRESULT_FROM_WIN32 ( GetLastError ( ) ) ;
2016-08-24 03:56:55 +03:00
auto_delete_file delonfail ( hFile . get ( ) ) ;
// Setup header
const size_t MAX_HEADER_SIZE = sizeof ( uint32_t ) + sizeof ( DDS_HEADER ) + sizeof ( DDS_HEADER_DXT10 ) ;
2018-03-16 21:33:06 +03:00
uint8_t fileHeader [ MAX_HEADER_SIZE ] ;
2016-08-24 03:56:55 +03:00
* reinterpret_cast < uint32_t * > ( & fileHeader [ 0 ] ) = DDS_MAGIC ;
2018-03-16 21:33:06 +03:00
auto header = reinterpret_cast < DDS_HEADER * > ( & fileHeader [ 0 ] + sizeof ( uint32_t ) ) ;
2016-08-24 03:56:55 +03:00
size_t headerSize = sizeof ( uint32_t ) + sizeof ( DDS_HEADER ) ;
2018-03-16 21:33:06 +03:00
memset ( header , 0 , sizeof ( DDS_HEADER ) ) ;
header - > size = sizeof ( DDS_HEADER ) ;
2016-08-24 03:56:55 +03:00
header - > flags = DDS_HEADER_FLAGS_TEXTURE | DDS_HEADER_FLAGS_MIPMAP ;
header - > height = desc . Height ;
header - > width = desc . Width ;
header - > mipMapCount = 1 ;
header - > caps = DDS_SURFACE_FLAGS_TEXTURE ;
// Try to use a legacy .DDS pixel format for better tools support, otherwise fallback to 'DX10' header extension
DDS_HEADER_DXT10 * extHeader = nullptr ;
2018-03-16 21:33:06 +03:00
switch ( desc . Format )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
case DXGI_FORMAT_R8G8B8A8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_A8B8G8R8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R16G16_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_G16R16 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R8G8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_A8L8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R16_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_L16 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_L8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_A8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_A8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R8G8_B8G8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_R8G8_B8G8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_G8R8_G8B8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_G8R8_G8B8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_BC1_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_DXT1 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_BC2_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_DXT3 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_BC3_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_DXT5 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_BC4_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_BC4_UNORM , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_BC4_SNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_BC4_SNORM , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_BC5_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_BC5_UNORM , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_BC5_SNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_BC5_SNORM , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_B5G6R5_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_R5G6B5 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_B5G5R5A1_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_A1R5G5B5 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R8G8_SNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_V8U8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R8G8B8A8_SNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_Q8W8V8U8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_R16G16_SNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_V16U16 , sizeof ( DDS_PIXELFORMAT ) ) ; break ;
case DXGI_FORMAT_B8G8R8A8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_A8R8G8B8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ; // DXGI 1.1
case DXGI_FORMAT_B8G8R8X8_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_X8R8G8B8 , sizeof ( DDS_PIXELFORMAT ) ) ; break ; // DXGI 1.1
case DXGI_FORMAT_YUY2 : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_YUY2 , sizeof ( DDS_PIXELFORMAT ) ) ; break ; // DXGI 1.2
case DXGI_FORMAT_B4G4R4A4_UNORM : memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_A4R4G4B4 , sizeof ( DDS_PIXELFORMAT ) ) ; break ; // DXGI 1.2
// Legacy D3DX formats using D3DFMT enum value as FourCC
case DXGI_FORMAT_R32G32B32A32_FLOAT : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 116 ; break ; // D3DFMT_A32B32G32R32F
case DXGI_FORMAT_R16G16B16A16_FLOAT : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 113 ; break ; // D3DFMT_A16B16G16R16F
case DXGI_FORMAT_R16G16B16A16_UNORM : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 36 ; break ; // D3DFMT_A16B16G16R16
case DXGI_FORMAT_R16G16B16A16_SNORM : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 110 ; break ; // D3DFMT_Q16W16V16U16
case DXGI_FORMAT_R32G32_FLOAT : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 115 ; break ; // D3DFMT_G32R32F
case DXGI_FORMAT_R16G16_FLOAT : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 112 ; break ; // D3DFMT_G16R16F
case DXGI_FORMAT_R32_FLOAT : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 114 ; break ; // D3DFMT_R32F
case DXGI_FORMAT_R16_FLOAT : header - > ddspf . size = sizeof ( DDS_PIXELFORMAT ) ; header - > ddspf . flags = DDS_FOURCC ; header - > ddspf . fourCC = 111 ; break ; // D3DFMT_R16F
case DXGI_FORMAT_AI44 :
case DXGI_FORMAT_IA44 :
case DXGI_FORMAT_P8 :
case DXGI_FORMAT_A8P8 :
2018-07-27 08:08:10 +03:00
DebugTrace ( " ERROR: ScreenGrab does not support video textures. Consider using DirectXTex. \n " ) ;
2018-03-16 21:33:06 +03:00
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
default :
memcpy_s ( & header - > ddspf , sizeof ( header - > ddspf ) , & DDSPF_DX10 , sizeof ( DDS_PIXELFORMAT ) ) ;
headerSize + = sizeof ( DDS_HEADER_DXT10 ) ;
2018-04-03 10:09:30 +03:00
extHeader = reinterpret_cast < DDS_HEADER_DXT10 * > ( fileHeader + sizeof ( uint32_t ) + sizeof ( DDS_HEADER ) ) ;
2018-03-16 21:33:06 +03:00
memset ( extHeader , 0 , sizeof ( DDS_HEADER_DXT10 ) ) ;
extHeader - > dxgiFormat = desc . Format ;
extHeader - > resourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D ;
extHeader - > arraySize = 1 ;
break ;
2016-08-24 03:56:55 +03:00
}
size_t rowPitch , slicePitch , rowCount ;
2018-08-04 02:06:03 +03:00
hr = GetSurfaceInfo ( desc . Width , desc . Height , desc . Format , & slicePitch , & rowPitch , & rowCount ) ;
if ( FAILED ( hr ) )
return hr ;
2016-08-24 03:56:55 +03:00
2018-07-27 08:08:10 +03:00
if ( rowPitch > UINT32_MAX | | slicePitch > UINT32_MAX )
return HRESULT_FROM_WIN32 ( ERROR_ARITHMETIC_OVERFLOW ) ;
2018-03-16 21:33:06 +03:00
if ( IsCompressed ( desc . Format ) )
2016-08-24 03:56:55 +03:00
{
header - > flags | = DDS_HEADER_FLAGS_LINEARSIZE ;
2018-03-16 21:33:06 +03:00
header - > pitchOrLinearSize = static_cast < uint32_t > ( slicePitch ) ;
2016-08-24 03:56:55 +03:00
}
else
{
header - > flags | = DDS_HEADER_FLAGS_PITCH ;
2018-03-16 21:33:06 +03:00
header - > pitchOrLinearSize = static_cast < uint32_t > ( rowPitch ) ;
2016-08-24 03:56:55 +03:00
}
// Setup pixels
2018-03-16 21:33:06 +03:00
std : : unique_ptr < uint8_t [ ] > pixels ( new ( std : : nothrow ) uint8_t [ slicePitch ] ) ;
2016-08-24 03:56:55 +03:00
if ( ! pixels )
return E_OUTOFMEMORY ;
D3D11_MAPPED_SUBRESOURCE mapped ;
2018-03-16 21:33:06 +03:00
hr = pContext - > Map ( pStaging . Get ( ) , 0 , D3D11_MAP_READ , 0 , & mapped ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-04-03 10:09:30 +03:00
auto sptr = static_cast < const uint8_t * > ( mapped . pData ) ;
2018-03-16 21:33:06 +03:00
if ( ! sptr )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
pContext - > Unmap ( pStaging . Get ( ) , 0 ) ;
2016-08-24 03:56:55 +03:00
return E_POINTER ;
}
uint8_t * dptr = pixels . get ( ) ;
2018-03-16 21:33:06 +03:00
size_t msize = std : : min < size_t > ( rowPitch , mapped . RowPitch ) ;
for ( size_t h = 0 ; h < rowCount ; + + h )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
memcpy_s ( dptr , rowPitch , sptr , msize ) ;
2016-08-24 03:56:55 +03:00
sptr + = mapped . RowPitch ;
dptr + = rowPitch ;
}
2018-03-16 21:33:06 +03:00
pContext - > Unmap ( pStaging . Get ( ) , 0 ) ;
2016-08-24 03:56:55 +03:00
// Write header & pixels
DWORD bytesWritten ;
2018-03-16 21:33:06 +03:00
if ( ! WriteFile ( hFile . get ( ) , fileHeader , static_cast < DWORD > ( headerSize ) , & bytesWritten , nullptr ) )
return HRESULT_FROM_WIN32 ( GetLastError ( ) ) ;
2016-08-24 03:56:55 +03:00
2018-03-16 21:33:06 +03:00
if ( bytesWritten ! = headerSize )
2016-08-24 03:56:55 +03:00
return E_FAIL ;
2018-03-16 21:33:06 +03:00
if ( ! WriteFile ( hFile . get ( ) , pixels . get ( ) , static_cast < DWORD > ( slicePitch ) , & bytesWritten , nullptr ) )
return HRESULT_FROM_WIN32 ( GetLastError ( ) ) ;
2016-08-24 03:56:55 +03:00
2018-03-16 21:33:06 +03:00
if ( bytesWritten ! = slicePitch )
2016-08-24 03:56:55 +03:00
return E_FAIL ;
delonfail . clear ( ) ;
return S_OK ;
}
//--------------------------------------------------------------------------------------
namespace DirectX
{
2018-03-16 21:33:06 +03:00
extern bool _IsWIC2 ( ) ;
extern IWICImagingFactory * _GetWIC ( ) ;
2016-08-24 03:56:55 +03:00
}
_Use_decl_annotations_
2018-03-16 21:33:06 +03:00
HRESULT DirectX : : SaveWICTextureToFile (
ID3D11DeviceContext * pContext ,
ID3D11Resource * pSource ,
REFGUID guidContainerFormat ,
const wchar_t * fileName ,
const GUID * targetFormat ,
2019-09-27 21:31:31 +03:00
std : : function < void ( IPropertyBag2 * ) > setCustomProps ,
2019-12-11 04:23:15 +03:00
bool forceSRGB ) noexcept
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
if ( ! fileName )
2016-08-24 03:56:55 +03:00
return E_INVALIDARG ;
D3D11_TEXTURE2D_DESC desc = { } ;
ComPtr < ID3D11Texture2D > pStaging ;
2018-03-16 21:33:06 +03:00
HRESULT hr = CaptureTexture ( pContext , pSource , desc , pStaging ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
// Determine source format's WIC equivalent
WICPixelFormatGUID pfGuid ;
2019-09-27 21:31:31 +03:00
bool sRGB = forceSRGB ;
2018-03-16 21:33:06 +03:00
switch ( desc . Format )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
case DXGI_FORMAT_R32G32B32A32_FLOAT : pfGuid = GUID_WICPixelFormat128bppRGBAFloat ; break ;
case DXGI_FORMAT_R16G16B16A16_FLOAT : pfGuid = GUID_WICPixelFormat64bppRGBAHalf ; break ;
case DXGI_FORMAT_R16G16B16A16_UNORM : pfGuid = GUID_WICPixelFormat64bppRGBA ; break ;
case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM : pfGuid = GUID_WICPixelFormat32bppRGBA1010102XR ; break ; // DXGI 1.1
case DXGI_FORMAT_R10G10B10A2_UNORM : pfGuid = GUID_WICPixelFormat32bppRGBA1010102 ; break ;
case DXGI_FORMAT_B5G5R5A1_UNORM : pfGuid = GUID_WICPixelFormat16bppBGRA5551 ; break ;
case DXGI_FORMAT_B5G6R5_UNORM : pfGuid = GUID_WICPixelFormat16bppBGR565 ; break ;
case DXGI_FORMAT_R32_FLOAT : pfGuid = GUID_WICPixelFormat32bppGrayFloat ; break ;
case DXGI_FORMAT_R16_FLOAT : pfGuid = GUID_WICPixelFormat16bppGrayHalf ; break ;
case DXGI_FORMAT_R16_UNORM : pfGuid = GUID_WICPixelFormat16bppGray ; break ;
case DXGI_FORMAT_R8_UNORM : pfGuid = GUID_WICPixelFormat8bppGray ; break ;
case DXGI_FORMAT_A8_UNORM : pfGuid = GUID_WICPixelFormat8bppAlpha ; break ;
case DXGI_FORMAT_R8G8B8A8_UNORM :
pfGuid = GUID_WICPixelFormat32bppRGBA ;
break ;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB :
pfGuid = GUID_WICPixelFormat32bppRGBA ;
sRGB = true ;
break ;
case DXGI_FORMAT_B8G8R8A8_UNORM : // DXGI 1.1
pfGuid = GUID_WICPixelFormat32bppBGRA ;
break ;
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : // DXGI 1.1
pfGuid = GUID_WICPixelFormat32bppBGRA ;
sRGB = true ;
break ;
case DXGI_FORMAT_B8G8R8X8_UNORM : // DXGI 1.1
pfGuid = GUID_WICPixelFormat32bppBGR ;
break ;
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB : // DXGI 1.1
pfGuid = GUID_WICPixelFormat32bppBGR ;
sRGB = true ;
break ;
default :
2018-07-27 08:08:10 +03:00
DebugTrace ( " ERROR: ScreenGrab does not support all DXGI formats (%u). Consider using DirectXTex. \n " , static_cast < uint32_t > ( desc . Format ) ) ;
2018-03-16 21:33:06 +03:00
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
2016-08-24 03:56:55 +03:00
}
auto pWIC = _GetWIC ( ) ;
2018-03-16 21:33:06 +03:00
if ( ! pWIC )
2016-08-24 03:56:55 +03:00
return E_NOINTERFACE ;
ComPtr < IWICStream > stream ;
2018-03-16 21:33:06 +03:00
hr = pWIC - > CreateStream ( stream . GetAddressOf ( ) ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-03-16 21:33:06 +03:00
hr = stream - > InitializeFromFilename ( fileName , GENERIC_WRITE ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
auto_delete_file_wic delonfail ( stream , fileName ) ;
ComPtr < IWICBitmapEncoder > encoder ;
2018-06-11 22:23:08 +03:00
hr = pWIC - > CreateEncoder ( guidContainerFormat , nullptr , encoder . GetAddressOf ( ) ) ;
2018-03-16 21:33:06 +03:00
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-03-16 21:33:06 +03:00
hr = encoder - > Initialize ( stream . Get ( ) , WICBitmapEncoderNoCache ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
ComPtr < IWICBitmapFrameEncode > frame ;
ComPtr < IPropertyBag2 > props ;
2018-03-16 21:33:06 +03:00
hr = encoder - > CreateNewFrame ( frame . GetAddressOf ( ) , props . GetAddressOf ( ) ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-03-16 21:33:06 +03:00
if ( targetFormat & & memcmp ( & guidContainerFormat , & GUID_ContainerFormatBmp , sizeof ( WICPixelFormatGUID ) ) = = 0 & & _IsWIC2 ( ) )
2016-08-24 03:56:55 +03:00
{
// Opt-in to the WIC2 support for writing 32-bit Windows BMP files with an alpha channel
PROPBAG2 option = { } ;
2017-01-11 01:50:52 +03:00
option . pstrName = const_cast < wchar_t * > ( L " EnableV5Header32bppBGRA " ) ;
2016-08-24 03:56:55 +03:00
2018-03-16 21:33:06 +03:00
VARIANT varValue ;
2016-08-24 03:56:55 +03:00
varValue . vt = VT_BOOL ;
2018-03-16 21:33:06 +03:00
varValue . boolVal = VARIANT_TRUE ;
( void ) props - > Write ( 1 , & option , & varValue ) ;
2016-08-24 03:56:55 +03:00
}
2018-03-16 21:33:06 +03:00
if ( setCustomProps )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
setCustomProps ( props . Get ( ) ) ;
2016-08-24 03:56:55 +03:00
}
2018-03-16 21:33:06 +03:00
hr = frame - > Initialize ( props . Get ( ) ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-03-16 21:33:06 +03:00
hr = frame - > SetSize ( desc . Width , desc . Height ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-03-16 21:33:06 +03:00
hr = frame - > SetResolution ( 72 , 72 ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
// Pick a target format
WICPixelFormatGUID targetGuid ;
2018-03-16 21:33:06 +03:00
if ( targetFormat )
2016-08-24 03:56:55 +03:00
{
targetGuid = * targetFormat ;
}
else
{
2018-03-16 23:45:47 +03:00
// Screenshots don't typically include the alpha channel of the render target
2018-03-16 21:33:06 +03:00
switch ( desc . Format )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
# if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
case DXGI_FORMAT_R32G32B32A32_FLOAT :
case DXGI_FORMAT_R16G16B16A16_FLOAT :
if ( _IsWIC2 ( ) )
{
targetGuid = GUID_WICPixelFormat96bppRGBFloat ;
}
else
{
targetGuid = GUID_WICPixelFormat24bppBGR ;
}
break ;
# endif
case DXGI_FORMAT_R16G16B16A16_UNORM : targetGuid = GUID_WICPixelFormat48bppBGR ; break ;
case DXGI_FORMAT_B5G5R5A1_UNORM : targetGuid = GUID_WICPixelFormat16bppBGR555 ; break ;
case DXGI_FORMAT_B5G6R5_UNORM : targetGuid = GUID_WICPixelFormat16bppBGR565 ; break ;
case DXGI_FORMAT_R32_FLOAT :
case DXGI_FORMAT_R16_FLOAT :
case DXGI_FORMAT_R16_UNORM :
case DXGI_FORMAT_R8_UNORM :
case DXGI_FORMAT_A8_UNORM :
targetGuid = GUID_WICPixelFormat8bppGray ;
break ;
default :
2016-08-24 03:56:55 +03:00
targetGuid = GUID_WICPixelFormat24bppBGR ;
2018-03-16 21:33:06 +03:00
break ;
2016-08-24 03:56:55 +03:00
}
}
2018-03-16 21:33:06 +03:00
hr = frame - > SetPixelFormat ( & targetGuid ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-03-16 21:33:06 +03:00
if ( targetFormat & & memcmp ( targetFormat , & targetGuid , sizeof ( WICPixelFormatGUID ) ) ! = 0 )
2016-08-24 03:56:55 +03:00
{
// Requested output pixel format is not supported by the WIC codec
return E_FAIL ;
}
// Encode WIC metadata
ComPtr < IWICMetadataQueryWriter > metawriter ;
2018-03-16 21:33:06 +03:00
if ( SUCCEEDED ( frame - > GetMetadataQueryWriter ( metawriter . GetAddressOf ( ) ) ) )
2016-08-24 03:56:55 +03:00
{
PROPVARIANT value ;
2018-03-16 21:33:06 +03:00
PropVariantInit ( & value ) ;
2016-08-24 03:56:55 +03:00
value . vt = VT_LPSTR ;
2017-01-11 01:50:52 +03:00
value . pszVal = const_cast < char * > ( " DirectXTK " ) ;
2016-08-24 03:56:55 +03:00
2018-03-16 21:33:06 +03:00
if ( memcmp ( & guidContainerFormat , & GUID_ContainerFormatPng , sizeof ( GUID ) ) = = 0 )
2016-08-24 03:56:55 +03:00
{
// Set Software name
2018-03-16 21:33:06 +03:00
( void ) metawriter - > SetMetadataByName ( L " /tEXt/{str=Software} " , & value ) ;
2016-08-24 03:56:55 +03:00
// Set sRGB chunk
2017-09-19 21:39:46 +03:00
if ( sRGB )
2016-08-24 03:56:55 +03:00
{
value . vt = VT_UI1 ;
value . bVal = 0 ;
2017-09-19 21:39:46 +03:00
( void ) metawriter - > SetMetadataByName ( L " /sRGB/RenderingIntent " , & value ) ;
}
else
{
// add gAMA chunk with gamma 1.0
value . vt = VT_UI4 ;
value . uintVal = 100000 ; // gama value * 100,000 -- i.e. gamma 1.0
( void ) metawriter - > SetMetadataByName ( L " /gAMA/ImageGamma " , & value ) ;
// remove sRGB chunk which is added by default.
( void ) metawriter - > RemoveMetadataByName ( L " /sRGB/RenderingIntent " ) ;
2016-08-24 03:56:55 +03:00
}
}
2018-03-16 21:33:06 +03:00
# if defined(_XBOX_ONE) && defined(_TITLE)
else if ( memcmp ( & guidContainerFormat , & GUID_ContainerFormatJpeg , sizeof ( GUID ) ) = = 0 )
2016-08-24 03:56:55 +03:00
{
// Set Software name
2018-03-16 21:33:06 +03:00
( void ) metawriter - > SetMetadataByName ( L " /app1/ifd/{ushort=305} " , & value ) ;
2016-08-24 03:56:55 +03:00
2018-03-16 21:33:06 +03:00
if ( sRGB )
2016-08-24 03:56:55 +03:00
{
// Set EXIF Colorspace of sRGB
value . vt = VT_UI2 ;
value . uiVal = 1 ;
2018-03-16 21:33:06 +03:00
( void ) metawriter - > SetMetadataByName ( L " /app1/ifd/exif/{ushort=40961} " , & value ) ;
2016-08-24 03:56:55 +03:00
}
}
2018-03-16 21:33:06 +03:00
else if ( memcmp ( & guidContainerFormat , & GUID_ContainerFormatTiff , sizeof ( GUID ) ) = = 0 )
2016-08-24 03:56:55 +03:00
{
// Set Software name
2018-03-16 21:33:06 +03:00
( void ) metawriter - > SetMetadataByName ( L " /ifd/{ushort=305} " , & value ) ;
2016-08-24 03:56:55 +03:00
2018-03-16 21:33:06 +03:00
if ( sRGB )
2016-08-24 03:56:55 +03:00
{
// Set EXIF Colorspace of sRGB
value . vt = VT_UI2 ;
value . uiVal = 1 ;
2018-03-16 21:33:06 +03:00
( void ) metawriter - > SetMetadataByName ( L " /ifd/exif/{ushort=40961} " , & value ) ;
2016-08-24 03:56:55 +03:00
}
}
2018-03-16 21:33:06 +03:00
# else
2016-08-24 03:56:55 +03:00
else
{
// Set Software name
2018-03-16 21:33:06 +03:00
( void ) metawriter - > SetMetadataByName ( L " System.ApplicationName " , & value ) ;
2016-08-24 03:56:55 +03:00
2018-03-16 21:33:06 +03:00
if ( sRGB )
2016-08-24 03:56:55 +03:00
{
// Set EXIF Colorspace of sRGB
value . vt = VT_UI2 ;
value . uiVal = 1 ;
2018-03-16 21:33:06 +03:00
( void ) metawriter - > SetMetadataByName ( L " System.Image.ColorSpace " , & value ) ;
2016-08-24 03:56:55 +03:00
}
}
2018-03-16 21:33:06 +03:00
# endif
2016-08-24 03:56:55 +03:00
}
D3D11_MAPPED_SUBRESOURCE mapped ;
2018-03-16 21:33:06 +03:00
hr = pContext - > Map ( pStaging . Get ( ) , 0 , D3D11_MAP_READ , 0 , & mapped ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
2018-03-16 21:33:06 +03:00
if ( memcmp ( & targetGuid , & pfGuid , sizeof ( WICPixelFormatGUID ) ) ! = 0 )
2016-08-24 03:56:55 +03:00
{
// Conversion required to write
ComPtr < IWICBitmap > source ;
2018-03-16 21:33:06 +03:00
hr = pWIC - > CreateBitmapFromMemory ( desc . Width , desc . Height , pfGuid ,
2019-09-27 23:13:34 +03:00
mapped . RowPitch , mapped . RowPitch * desc . Height ,
static_cast < BYTE * > ( mapped . pData ) , source . GetAddressOf ( ) ) ;
2018-03-16 21:33:06 +03:00
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
pContext - > Unmap ( pStaging . Get ( ) , 0 ) ;
2016-08-24 03:56:55 +03:00
return hr ;
}
ComPtr < IWICFormatConverter > FC ;
2018-03-16 21:33:06 +03:00
hr = pWIC - > CreateFormatConverter ( FC . GetAddressOf ( ) ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
pContext - > Unmap ( pStaging . Get ( ) , 0 ) ;
2016-08-24 03:56:55 +03:00
return hr ;
}
BOOL canConvert = FALSE ;
2018-03-16 21:33:06 +03:00
hr = FC - > CanConvert ( pfGuid , targetGuid , & canConvert ) ;
if ( FAILED ( hr ) | | ! canConvert )
2016-08-24 03:56:55 +03:00
{
return E_UNEXPECTED ;
}
2018-03-16 21:33:06 +03:00
hr = FC - > Initialize ( source . Get ( ) , targetGuid , WICBitmapDitherTypeNone , nullptr , 0 , WICBitmapPaletteTypeMedianCut ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
pContext - > Unmap ( pStaging . Get ( ) , 0 ) ;
2016-08-24 03:56:55 +03:00
return hr ;
}
2018-03-16 21:33:06 +03:00
WICRect rect = { 0 , 0 , static_cast < INT > ( desc . Width ) , static_cast < INT > ( desc . Height ) } ;
hr = frame - > WriteSource ( FC . Get ( ) , & rect ) ;
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
{
2018-03-16 21:33:06 +03:00
pContext - > Unmap ( pStaging . Get ( ) , 0 ) ;
2016-08-24 03:56:55 +03:00
return hr ;
}
}
else
{
// No conversion required
2018-04-03 10:09:30 +03:00
hr = frame - > WritePixels ( desc . Height , mapped . RowPitch , mapped . RowPitch * desc . Height , static_cast < BYTE * > ( mapped . pData ) ) ;
2018-03-16 21:33:06 +03:00
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
}
2018-03-16 21:33:06 +03:00
pContext - > Unmap ( pStaging . Get ( ) , 0 ) ;
2016-08-24 03:56:55 +03:00
hr = frame - > Commit ( ) ;
2018-03-16 21:33:06 +03:00
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
hr = encoder - > Commit ( ) ;
2018-03-16 21:33:06 +03:00
if ( FAILED ( hr ) )
2016-08-24 03:56:55 +03:00
return hr ;
delonfail . clear ( ) ;
return S_OK ;
}