1
0
mirror of https://github.com/microsoft/DirectXTex synced 2024-11-21 20:10:05 +00:00
DirectXTex/WICTextureLoader/WICTextureLoader11.cpp

1095 lines
38 KiB
C++

//--------------------------------------------------------------------------------------
// File: WICTextureLoader11.cpp
//
// Function for loading a WIC image and creating a Direct3D runtime texture for it
// (auto-generating mipmaps if possible)
//
// Note: Assumes application has already called CoInitializeEx
//
// 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.
//
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkId=248926
// http://go.microsoft.com/fwlink/?LinkId=248929
//--------------------------------------------------------------------------------------
// We could load multi-frame images (TIFF/GIF) into a texture array.
// For now, we just load the first frame (note: DirectXTex supports multi-frame images)
#include "WICTextureLoader11.h"
#include <dxgiformat.h>
#include <wincodec.h>
#include <wrl\client.h>
#include <algorithm>
#include <cassert>
#include <cfloat>
#include <cmath>
#include <cstring>
#include <iterator>
#include <memory>
#include <new>
#include <tuple>
#ifdef _MSC_VER
// Off by default warnings
#pragma warning(disable : 4619 4616 4061 4062 4623 4626 5027)
// C4619/4616 #pragma warning warnings
// C4061 enumerator 'x' in switch of enum 'y' is not explicitly handled by a case label
// C4062 enumerator 'x' in switch of enum 'y' is not handled
// C4623 default constructor was implicitly defined as deleted
// C4626 assignment operator was implicitly defined as deleted
// C5027 move assignment operator was implicitly defined as deleted
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wcovered-switch-default"
#pragma clang diagnostic ignored "-Wswitch-enum"
#endif
using namespace DirectX;
using Microsoft::WRL::ComPtr;
namespace
{
//--------------------------------------------------------------------------------------
#if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
template<UINT TNameLength>
inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_ const char(&name)[TNameLength]) noexcept
{
resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name);
}
#else
template<UINT TNameLength>
inline void SetDebugObjectName(_In_ ID3D11DeviceChild*, _In_ const char(&)[TNameLength]) noexcept
{
}
#endif
//-------------------------------------------------------------------------------------
// WIC Pixel Format Translation Data
//-------------------------------------------------------------------------------------
struct WICTranslate
{
const GUID& wic;
DXGI_FORMAT format;
constexpr WICTranslate(const GUID& wg, DXGI_FORMAT fmt) noexcept :
wic(wg),
format(fmt) {}
};
constexpr 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_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 },
};
//-------------------------------------------------------------------------------------
// WIC Pixel Format nearest conversion table
//-------------------------------------------------------------------------------------
struct WICConvert
{
const GUID& source;
const GUID& target;
constexpr WICConvert(const GUID& src, const GUID& tgt) noexcept :
source(src),
target(tgt) {}
};
constexpr 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
{ 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_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_WICPixelFormat32bppRGBE, 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_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM
{ GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
{ 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
};
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
bool g_WIC2 = false;
#endif
BOOL WINAPI InitializeWICFactory(PINIT_ONCE, PVOID, PVOID *ifactory) noexcept
{
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
HRESULT hr = CoCreateInstance(
CLSID_WICImagingFactory2,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory2),
ifactory
);
if (SUCCEEDED(hr))
{
// WIC2 is available on Windows 10, Windows 8.x, and Windows 7 SP1 with KB 2670838 installed
g_WIC2 = true;
return TRUE;
}
else
{
hr = CoCreateInstance(
CLSID_WICImagingFactory1,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
ifactory
);
return SUCCEEDED(hr) ? TRUE : FALSE;
}
#else
return SUCCEEDED(CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
ifactory)) ? TRUE : FALSE;
#endif
}
IWICImagingFactory* GetWIC() noexcept
{
static INIT_ONCE s_initOnce = INIT_ONCE_STATIC_INIT;
IWICImagingFactory* factory = nullptr;
if (!InitOnceExecuteOnce(
&s_initOnce,
InitializeWICFactory,
nullptr,
reinterpret_cast<LPVOID*>(&factory)))
{
return nullptr;
}
return factory;
}
//---------------------------------------------------------------------------------
DXGI_FORMAT WICToDXGI(const GUID& guid) noexcept
{
for (size_t i = 0; i < std::size(g_WICFormats); ++i)
{
if (memcmp(&g_WICFormats[i].wic, &guid, sizeof(GUID)) == 0)
return g_WICFormats[i].format;
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
if (g_WIC2)
{
if (memcmp(&GUID_WICPixelFormat96bppRGBFloat, &guid, sizeof(GUID)) == 0)
return DXGI_FORMAT_R32G32B32_FLOAT;
}
#endif
return DXGI_FORMAT_UNKNOWN;
}
//---------------------------------------------------------------------------------
size_t WICBitsPerPixel(REFGUID targetGuid) noexcept
{
auto pWIC = GetWIC();
if (!pWIC)
return 0;
ComPtr<IWICComponentInfo> cinfo;
if (FAILED(pWIC->CreateComponentInfo(targetGuid, cinfo.GetAddressOf())))
return 0;
WICComponentType type;
if (FAILED(cinfo->GetComponentType(&type)))
return 0;
if (type != WICPixelFormat)
return 0;
ComPtr<IWICPixelFormatInfo> pfinfo;
if (FAILED(cinfo.As(&pfinfo)))
return 0;
UINT bpp;
if (FAILED(pfinfo->GetBitsPerPixel(&bpp)))
return 0;
return bpp;
}
//--------------------------------------------------------------------------------------
DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) noexcept
{
switch (format)
{
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 format;
}
}
//---------------------------------------------------------------------------------
void FitPowerOf2(UINT origx, UINT origy, UINT& targetx, UINT& targety, size_t maxsize)
{
const float origAR = float(origx) / float(origy);
if (origx > origy)
{
size_t x;
for (x = maxsize; x > 1; x >>= 1) { if (x <= targetx) break; }
targetx = UINT(x);
float bestScore = FLT_MAX;
for (size_t y = maxsize; y > 0; y >>= 1)
{
const float score = fabsf((float(x) / float(y)) - origAR);
if (score < bestScore)
{
bestScore = score;
targety = UINT(y);
}
}
}
else
{
size_t y;
for (y = maxsize; y > 1; y >>= 1) { if (y <= targety) break; }
targety = UINT(y);
float bestScore = FLT_MAX;
for (size_t x = maxsize; x > 0; x >>= 1)
{
const float score = fabsf((float(x) / float(y)) - origAR);
if (score < bestScore)
{
bestScore = score;
targetx = UINT(x);
}
}
}
}
//---------------------------------------------------------------------------------
HRESULT CreateTextureFromWIC(_In_ ID3D11Device* d3dDevice,
_In_opt_ ID3D11DeviceContext* d3dContext,
_In_ IWICBitmapFrameDecode* frame,
_In_ size_t maxsize,
_In_ D3D11_USAGE usage,
_In_ unsigned int bindFlags,
_In_ unsigned int cpuAccessFlags,
_In_ unsigned int miscFlags,
_In_ WIC_LOADER_FLAGS loadFlags,
_Outptr_opt_ ID3D11Resource** texture,
_Outptr_opt_ ID3D11ShaderResourceView** textureView) noexcept
{
UINT width, height;
HRESULT hr = frame->GetSize(&width, &height);
if (FAILED(hr))
return hr;
if (maxsize > UINT32_MAX)
return E_INVALIDARG;
assert(width > 0 && height > 0);
if (!maxsize)
{
// 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 = 2048u /*D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
break;
case D3D_FEATURE_LEVEL_9_3:
maxsize = 4096u /*D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
break;
case D3D_FEATURE_LEVEL_10_0:
case D3D_FEATURE_LEVEL_10_1:
maxsize = 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/;
break;
default:
maxsize = size_t(D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION);
break;
}
}
assert(maxsize > 0);
UINT twidth = width;
UINT theight = height;
if (loadFlags & WIC_LOADER_FIT_POW2)
{
FitPowerOf2(width, height, twidth, theight, maxsize);
}
else if (width > maxsize || height > maxsize)
{
const float ar = static_cast<float>(height) / static_cast<float>(width);
if (width > height)
{
twidth = static_cast<UINT>(maxsize);
theight = std::max<UINT>(1, static_cast<UINT>(static_cast<float>(maxsize) * ar));
}
else
{
theight = static_cast<UINT>(maxsize);
twidth = std::max<UINT>(1, static_cast<UINT>(static_cast<float>(maxsize) / ar));
}
assert(twidth <= maxsize && theight <= maxsize);
}
if (loadFlags & WIC_LOADER_MAKE_SQUARE)
{
twidth = std::max<UINT>(twidth, theight);
theight = twidth;
}
// Determine format
WICPixelFormatGUID pixelFormat;
hr = frame->GetPixelFormat(&pixelFormat);
if (FAILED(hr))
return hr;
WICPixelFormatGUID convertGUID;
memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &pixelFormat, sizeof(GUID));
size_t bpp = 0;
DXGI_FORMAT format = WICToDXGI(pixelFormat);
if (format == DXGI_FORMAT_UNKNOWN)
{
if (memcmp(&GUID_WICPixelFormat96bppRGBFixedPoint, &pixelFormat, sizeof(WICPixelFormatGUID)) == 0)
{
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
if (g_WIC2)
{
memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &GUID_WICPixelFormat96bppRGBFloat, sizeof(GUID));
format = DXGI_FORMAT_R32G32B32_FLOAT;
bpp = 96;
}
else
#endif
{
memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &GUID_WICPixelFormat128bppRGBAFloat, sizeof(GUID));
format = DXGI_FORMAT_R32G32B32A32_FLOAT;
bpp = 128;
}
}
else
{
for (size_t i = 0; i < std::size(g_WICConvert); ++i)
{
if (memcmp(&g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID)) == 0)
{
memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &g_WICConvert[i].target, sizeof(GUID));
format = 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 (_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
if ((format == DXGI_FORMAT_R32G32B32_FLOAT) && d3dContext && textureView)
{
// Special case test for optional device support for autogen mipchains for R32G32B32_FLOAT
UINT fmtSupport = 0;
hr = d3dDevice->CheckFormatSupport(DXGI_FORMAT_R32G32B32_FLOAT, &fmtSupport);
if (FAILED(hr) || !(fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN))
{
// Use R32G32B32A32_FLOAT instead which is required for Feature Level 10.0 and up
memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &GUID_WICPixelFormat128bppRGBAFloat, sizeof(GUID));
format = DXGI_FORMAT_R32G32B32A32_FLOAT;
bpp = 128;
}
}
#endif
if (loadFlags & WIC_LOADER_FORCE_RGBA32)
{
memcpy_s(&convertGUID, sizeof(WICPixelFormatGUID), &GUID_WICPixelFormat32bppRGBA, sizeof(GUID));
format = DXGI_FORMAT_R8G8B8A8_UNORM;
bpp = 32;
}
if (!bpp)
return E_FAIL;
// Handle sRGB formats
if (loadFlags & WIC_LOADER_FORCE_SRGB)
{
format = MakeSRGB(format);
}
else if (!(loadFlags & WIC_LOADER_IGNORE_SRGB))
{
ComPtr<IWICMetadataQueryReader> metareader;
if (SUCCEEDED(frame->GetMetadataQueryReader(metareader.GetAddressOf())))
{
GUID containerFormat;
if (SUCCEEDED(metareader->GetContainerFormat(&containerFormat)))
{
bool sRGB = false;
PROPVARIANT value;
PropVariantInit(&value);
// Check for colorspace chunks
if (memcmp(&containerFormat, &GUID_ContainerFormatPng, sizeof(GUID)) == 0)
{
if (SUCCEEDED(metareader->GetMetadataByName(L"/sRGB/RenderingIntent", &value)) && value.vt == VT_UI1)
{
sRGB = true;
}
else if (SUCCEEDED(metareader->GetMetadataByName(L"/gAMA/ImageGamma", &value)) && value.vt == VT_UI4)
{
sRGB = (value.uintVal == 45455);
}
else
{
sRGB = (loadFlags & WIC_LOADER_SRGB_DEFAULT) != 0;
}
}
else if (SUCCEEDED(metareader->GetMetadataByName(L"System.Image.ColorSpace", &value)) && value.vt == VT_UI2)
{
sRGB = (value.uiVal == 1);
}
else
{
sRGB = (loadFlags & WIC_LOADER_SRGB_DEFAULT) != 0;
}
std::ignore = PropVariantClear(&value);
if (sRGB)
format = MakeSRGB(format);
}
}
}
// 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_s(&convertGUID, sizeof(WICPixelFormatGUID), &GUID_WICPixelFormat32bppRGBA, sizeof(GUID));
format = DXGI_FORMAT_R8G8B8A8_UNORM;
bpp = 32;
}
// Allocate temporary memory for image
const uint64_t rowBytes = (uint64_t(twidth) * uint64_t(bpp) + 7u) / 8u;
const uint64_t numBytes = rowBytes * uint64_t(theight);
if (rowBytes > UINT32_MAX || numBytes > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
auto const rowPitch = static_cast<size_t>(rowBytes);
auto const imageSize = static_cast<size_t>(numBytes);
std::unique_ptr<uint8_t[]> temp(new (std::nothrow) uint8_t[imageSize]);
if (!temp)
return E_OUTOFMEMORY;
// Load image data
if (memcmp(&convertGUID, &pixelFormat, sizeof(GUID)) == 0
&& twidth == width
&& theight == height)
{
// No format conversion or resize needed
hr = frame->CopyPixels(nullptr, static_cast<UINT>(rowPitch), static_cast<UINT>(imageSize), temp.get());
if (FAILED(hr))
return hr;
}
else if (twidth != width || theight != height)
{
// Resize
auto pWIC = GetWIC();
if (!pWIC)
return E_NOINTERFACE;
ComPtr<IWICBitmapScaler> scaler;
hr = pWIC->CreateBitmapScaler(scaler.GetAddressOf());
if (FAILED(hr))
return hr;
hr = scaler->Initialize(frame, twidth, theight, WICBitmapInterpolationModeFant);
if (FAILED(hr))
return hr;
WICPixelFormatGUID pfScaler;
hr = scaler->GetPixelFormat(&pfScaler);
if (FAILED(hr))
return hr;
if (memcmp(&convertGUID, &pfScaler, sizeof(GUID)) == 0)
{
// No format conversion needed
hr = scaler->CopyPixels(nullptr, static_cast<UINT>(rowPitch), static_cast<UINT>(imageSize), temp.get());
if (FAILED(hr))
return hr;
}
else
{
ComPtr<IWICFormatConverter> FC;
hr = pWIC->CreateFormatConverter(FC.GetAddressOf());
if (FAILED(hr))
return hr;
BOOL canConvert = FALSE;
hr = FC->CanConvert(pfScaler, convertGUID, &canConvert);
if (FAILED(hr) || !canConvert)
{
return E_UNEXPECTED;
}
hr = FC->Initialize(scaler.Get(), convertGUID, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut);
if (FAILED(hr))
return hr;
hr = FC->CopyPixels(nullptr, static_cast<UINT>(rowPitch), static_cast<UINT>(imageSize), temp.get());
if (FAILED(hr))
return hr;
}
}
else
{
// Format conversion but no resize
auto pWIC = GetWIC();
if (!pWIC)
return E_NOINTERFACE;
ComPtr<IWICFormatConverter> FC;
hr = pWIC->CreateFormatConverter(FC.GetAddressOf());
if (FAILED(hr))
return hr;
BOOL canConvert = FALSE;
hr = FC->CanConvert(pixelFormat, convertGUID, &canConvert);
if (FAILED(hr) || !canConvert)
{
return E_UNEXPECTED;
}
hr = FC->Initialize(frame, convertGUID, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut);
if (FAILED(hr))
return hr;
hr = FC->CopyPixels(nullptr, static_cast<UINT>(rowPitch), static_cast<UINT>(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 && textureView) // 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) ? 0u : 1u;
desc.ArraySize = 1;
desc.Format = format;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = usage;
desc.CPUAccessFlags = cpuAccessFlags;
if (autogen)
{
desc.BindFlags = bindFlags | D3D11_BIND_RENDER_TARGET;
desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_GENERATE_MIPS;
}
else
{
desc.BindFlags = bindFlags;
desc.MiscFlags = miscFlags;
}
D3D11_SUBRESOURCE_DATA initData;
initData.pSysMem = temp.get();
initData.SysMemPitch = static_cast<UINT>(rowPitch);
initData.SysMemSlicePitch = static_cast<UINT>(imageSize);
ID3D11Texture2D* tex = nullptr;
hr = d3dDevice->CreateTexture2D(&desc, (autogen) ? nullptr : &initData, &tex);
if (SUCCEEDED(hr) && tex)
{
if (textureView)
{
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {};
SRVDesc.Format = desc.Format;
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MipLevels = (autogen) ? unsigned(-1) : 1u;
hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, textureView);
if (FAILED(hr))
{
tex->Release();
return hr;
}
if (autogen)
{
assert(d3dContext != nullptr);
d3dContext->UpdateSubresource(tex, 0, nullptr, temp.get(), static_cast<UINT>(rowPitch), static_cast<UINT>(imageSize));
d3dContext->GenerateMips(*textureView);
}
}
if (texture)
{
*texture = tex;
}
else
{
SetDebugObjectName(tex, "WICTextureLoader");
tex->Release();
}
}
return hr;
}
//--------------------------------------------------------------------------------------
void SetDebugTextureInfo(
_In_z_ const wchar_t* fileName,
_In_opt_ ID3D11Resource** texture,
_In_opt_ ID3D11ShaderResourceView** textureView) noexcept
{
#if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
if (texture || textureView)
{
CHAR strFileA[MAX_PATH];
const int result = WideCharToMultiByte(CP_UTF8,
WC_NO_BEST_FIT_CHARS,
fileName,
-1,
strFileA,
MAX_PATH,
nullptr,
nullptr
);
if (result > 0)
{
if (texture && *texture)
{
(*texture)->SetPrivateData(WKPDID_D3DDebugObjectName,
static_cast<UINT>(result),
strFileA
);
}
if (textureView && *textureView)
{
(*textureView)->SetPrivateData(WKPDID_D3DDebugObjectName,
static_cast<UINT>(result),
strFileA
);
}
}
}
#else
UNREFERENCED_PARAMETER(fileName);
UNREFERENCED_PARAMETER(texture);
UNREFERENCED_PARAMETER(textureView);
#endif
}
} // anonymous namespace
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromMemory(
ID3D11Device* d3dDevice,
const uint8_t* wicData,
size_t wicDataSize,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView,
size_t maxsize) noexcept
{
return CreateWICTextureFromMemoryEx(d3dDevice, nullptr,
wicData, wicDataSize,
maxsize,
D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0,
WIC_LOADER_DEFAULT,
texture, textureView);
}
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromMemory(
ID3D11Device* d3dDevice,
ID3D11DeviceContext* d3dContext,
const uint8_t* wicData,
size_t wicDataSize,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView,
size_t maxsize) noexcept
{
return CreateWICTextureFromMemoryEx(d3dDevice, d3dContext,
wicData, wicDataSize,
maxsize,
D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0,
WIC_LOADER_DEFAULT,
texture, textureView);
}
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromMemoryEx(
ID3D11Device* d3dDevice,
const uint8_t* wicData,
size_t wicDataSize,
size_t maxsize,
D3D11_USAGE usage,
unsigned int bindFlags,
unsigned int cpuAccessFlags,
unsigned int miscFlags,
WIC_LOADER_FLAGS loadFlags,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView) noexcept
{
return CreateWICTextureFromMemoryEx(d3dDevice, nullptr,
wicData, wicDataSize,
maxsize,
usage, bindFlags, cpuAccessFlags, miscFlags,
loadFlags,
texture, textureView);
}
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromMemoryEx(
ID3D11Device* d3dDevice,
ID3D11DeviceContext* d3dContext,
const uint8_t* wicData,
size_t wicDataSize,
size_t maxsize,
D3D11_USAGE usage,
unsigned int bindFlags,
unsigned int cpuAccessFlags,
unsigned int miscFlags,
WIC_LOADER_FLAGS loadFlags,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView) noexcept
{
if (texture)
{
*texture = nullptr;
}
if (textureView)
{
*textureView = nullptr;
}
if (!d3dDevice || !wicData || (!texture && !textureView))
{
return E_INVALIDARG;
}
if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE))
{
return E_INVALIDARG;
}
if (!wicDataSize)
return E_FAIL;
if (wicDataSize > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
auto pWIC = GetWIC();
if (!pWIC)
return E_NOINTERFACE;
// Create input stream for memory
ComPtr<IWICStream> stream;
HRESULT hr = pWIC->CreateStream(stream.GetAddressOf());
if (FAILED(hr))
return hr;
hr = stream->InitializeFromMemory(const_cast<uint8_t*>(wicData), static_cast<DWORD>(wicDataSize));
if (FAILED(hr))
return hr;
// Initialize WIC
ComPtr<IWICBitmapDecoder> decoder;
hr = pWIC->CreateDecoderFromStream(stream.Get(), nullptr, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf());
if (FAILED(hr))
return hr;
ComPtr<IWICBitmapFrameDecode> frame;
hr = decoder->GetFrame(0, frame.GetAddressOf());
if (FAILED(hr))
return hr;
hr = CreateTextureFromWIC(d3dDevice, d3dContext,
frame.Get(),
maxsize,
usage, bindFlags, cpuAccessFlags, miscFlags,
loadFlags,
texture, textureView);
if (FAILED(hr))
return hr;
if (texture && *texture)
{
SetDebugObjectName(*texture, "WICTextureLoader");
}
if (textureView && *textureView)
{
SetDebugObjectName(*textureView, "WICTextureLoader");
}
return hr;
}
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromFile(
ID3D11Device* d3dDevice,
const wchar_t* fileName,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView,
size_t maxsize) noexcept
{
return CreateWICTextureFromFileEx(d3dDevice, nullptr,
fileName, maxsize,
D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0,
WIC_LOADER_DEFAULT,
texture, textureView);
}
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromFile(
ID3D11Device* d3dDevice,
ID3D11DeviceContext* d3dContext,
const wchar_t* fileName,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView,
size_t maxsize) noexcept
{
return CreateWICTextureFromFileEx(d3dDevice, d3dContext,
fileName,
maxsize,
D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0,
WIC_LOADER_DEFAULT,
texture, textureView);
}
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromFileEx(
ID3D11Device* d3dDevice,
const wchar_t* fileName,
size_t maxsize,
D3D11_USAGE usage,
unsigned int bindFlags,
unsigned int cpuAccessFlags,
unsigned int miscFlags,
WIC_LOADER_FLAGS loadFlags,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView) noexcept
{
return CreateWICTextureFromFileEx(d3dDevice, nullptr,
fileName,
maxsize,
usage, bindFlags, cpuAccessFlags, miscFlags,
loadFlags,
texture, textureView);
}
_Use_decl_annotations_
HRESULT DirectX::CreateWICTextureFromFileEx(
ID3D11Device* d3dDevice,
ID3D11DeviceContext* d3dContext,
const wchar_t* fileName,
size_t maxsize,
D3D11_USAGE usage,
unsigned int bindFlags,
unsigned int cpuAccessFlags,
unsigned int miscFlags,
WIC_LOADER_FLAGS loadFlags,
ID3D11Resource** texture,
ID3D11ShaderResourceView** textureView) noexcept
{
if (texture)
{
*texture = nullptr;
}
if (textureView)
{
*textureView = nullptr;
}
if (!d3dDevice || !fileName || (!texture && !textureView))
{
return E_INVALIDARG;
}
if (textureView && !(bindFlags & D3D11_BIND_SHADER_RESOURCE))
{
return E_INVALIDARG;
}
auto pWIC = GetWIC();
if (!pWIC)
return E_NOINTERFACE;
// Initialize WIC
ComPtr<IWICBitmapDecoder> decoder;
HRESULT hr = pWIC->CreateDecoderFromFilename(fileName,
nullptr,
GENERIC_READ,
WICDecodeMetadataCacheOnDemand,
decoder.GetAddressOf());
if (FAILED(hr))
return hr;
ComPtr<IWICBitmapFrameDecode> frame;
hr = decoder->GetFrame(0, frame.GetAddressOf());
if (FAILED(hr))
return hr;
hr = CreateTextureFromWIC(d3dDevice, d3dContext,
frame.Get(),
maxsize,
usage, bindFlags, cpuAccessFlags, miscFlags,
loadFlags,
texture, textureView);
if (SUCCEEDED(hr))
{
SetDebugTextureInfo(fileName, texture, textureView);
}
return hr;
}