in DDSTextureLoader/DDSTextureLoader9.cpp [773:1085]
HRESULT CreateTextureFromDDS(
_In_ LPDIRECT3DDEVICE9 device,
_In_ const DDS_HEADER* header,
_In_reads_bytes_(bitSize) const uint8_t* bitData,
_In_ size_t bitSize,
_In_ DWORD usage,
_In_ D3DPOOL pool,
_Outptr_ LPDIRECT3DBASETEXTURE9* texture,
bool generateMipsIfMissing) noexcept
{
HRESULT hr = S_OK;
UINT iWidth = header->width;
UINT iHeight = header->height;
UINT iMipCount = header->mipMapCount;
if (0 == iMipCount)
{
iMipCount = 1;
}
// Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 10 hardware requirements)
if (iMipCount > 14u /*D3D10_REQ_MIP_LEVELS*/)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
// We could support a subset of 'DX10' extended header DDS files, but we'll assume here we are only
// supporting legacy DDS files for a Direct3D9 device
D3DFORMAT fmt = GetD3D9Format(header->ddspf);
if (fmt == D3DFMT_UNKNOWN || BitsPerPixel(fmt) == 0)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
if (header->flags & DDS_HEADER_FLAGS_VOLUME)
{
UINT iDepth = header->depth;
if ((iWidth > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/)
|| (iHeight > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/)
|| (iDepth > 2048u /*D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION*/))
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
// Create the volume texture (let the runtime do the validation)
ComPtr<IDirect3DVolumeTexture9> pTexture;
hr = device->CreateVolumeTexture(iWidth, iHeight, iDepth, iMipCount,
usage, fmt, pool, pTexture.GetAddressOf(), nullptr);
if (FAILED(hr))
return hr;
ComPtr<IDirect3DVolumeTexture9> pStagingTexture;
if (pool == D3DPOOL_DEFAULT)
{
hr = device->CreateVolumeTexture(iWidth, iHeight, iDepth, iMipCount,
0u, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr);
if (FAILED(hr))
return hr;
}
else
{
pStagingTexture = pTexture;
}
// Lock, fill, unlock
size_t NumBytes = 0;
size_t RowBytes = 0;
size_t NumRows = 0;
const uint8_t* pSrcBits = bitData;
const uint8_t* pEndBits = bitData + bitSize;
D3DLOCKED_BOX LockedBox = {};
for (UINT i = 0; i < iMipCount; ++i)
{
GetSurfaceInfo(iWidth, iHeight, fmt, &NumBytes, &RowBytes, &NumRows);
if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
if ((pSrcBits + (NumBytes * iDepth)) > pEndBits)
{
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
}
if (SUCCEEDED(pStagingTexture->LockBox(i, &LockedBox, nullptr, 0)))
{
auto pDestBits = static_cast<uint8_t*>(LockedBox.pBits);
for (UINT j = 0; j < iDepth; ++j)
{
uint8_t* dptr = pDestBits;
const uint8_t* sptr = pSrcBits;
// Copy stride line by line
for (size_t h = 0; h < NumRows; h++)
{
memcpy_s(dptr, static_cast<size_t>(LockedBox.RowPitch), sptr, RowBytes);
dptr += LockedBox.RowPitch;
sptr += RowBytes;
}
pDestBits += LockedBox.SlicePitch;
pSrcBits += NumBytes;
}
pStagingTexture->UnlockBox(i);
}
iWidth = iWidth >> 1;
iHeight = iHeight >> 1;
iDepth = iDepth >> 1;
if (iWidth == 0)
iWidth = 1;
if (iHeight == 0)
iHeight = 1;
if (iDepth == 0)
iDepth = 1;
}
if (pool == D3DPOOL_DEFAULT)
{
hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get());
if (FAILED(hr))
return hr;
}
*texture = pTexture.Detach();
}
else if (header->caps2 & DDS_CUBEMAP)
{
if ((iWidth > 8192u /*D3D10_REQ_TEXTURECUBE_DIMENSION*/)
|| (iHeight > 8192u /*D3D10_REQ_TEXTURECUBE_DIMENSION*/))
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
// We require at least one face to be defined, and the faces must be square
if ((header->caps2 & DDS_CUBEMAP_ALLFACES) == 0 || iHeight != iWidth)
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
// Create the cubemap (let the runtime do the validation)
ComPtr<IDirect3DCubeTexture9> pTexture;
hr = device->CreateCubeTexture(iWidth, iMipCount,
usage, fmt, pool, pTexture.GetAddressOf(), nullptr);
if (FAILED(hr))
return hr;
ComPtr<IDirect3DCubeTexture9> pStagingTexture;
if (pool == D3DPOOL_DEFAULT)
{
hr = device->CreateCubeTexture(iWidth, iMipCount,
0u, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr);
if (FAILED(hr))
return hr;
}
else
{
pStagingTexture = pTexture;
}
// Lock, fill, unlock
size_t NumBytes = 0;
size_t RowBytes = 0;
size_t NumRows = 0;
const uint8_t* pSrcBits = bitData;
const uint8_t* pEndBits = bitData + bitSize;
D3DLOCKED_RECT LockedRect = {};
UINT mask = DDS_CUBEMAP_POSITIVEX & ~DDS_CUBEMAP;
for (UINT f = 0; f < 6; ++f, mask <<= 1)
{
if (!(header->caps2 & mask))
continue;
UINT w = iWidth;
UINT h = iHeight;
for (UINT i = 0; i < iMipCount; ++i)
{
GetSurfaceInfo(w, h, fmt, &NumBytes, &RowBytes, &NumRows);
if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
if ((pSrcBits + NumBytes) > pEndBits)
{
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
}
if (SUCCEEDED(pStagingTexture->LockRect(static_cast<D3DCUBEMAP_FACES>(f), i, &LockedRect, nullptr, 0)))
{
auto pDestBits = static_cast<uint8_t*>(LockedRect.pBits);
// Copy stride line by line
for (size_t r = 0; r < NumRows; r++)
{
memcpy_s(pDestBits, static_cast<size_t>(LockedRect.Pitch), pSrcBits, RowBytes);
pDestBits += LockedRect.Pitch;
pSrcBits += RowBytes;
}
pStagingTexture->UnlockRect(static_cast<D3DCUBEMAP_FACES>(f), i);
}
w = w >> 1;
h = h >> 1;
if (w == 0)
w = 1;
if (h == 0)
h = 1;
}
}
if (pool == D3DPOOL_DEFAULT)
{
hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get());
if (FAILED(hr))
return hr;
}
*texture = pTexture.Detach();
}
else
{
if ((iWidth > 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/)
|| (iHeight > 8192u /*D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION*/))
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
// Create the texture (let the runtime do the validation)
if (generateMipsIfMissing)
usage |= D3DUSAGE_AUTOGENMIPMAP;
ComPtr<IDirect3DTexture9> pTexture;
hr = device->CreateTexture(iWidth, iHeight, iMipCount,
usage, fmt, pool,
pTexture.GetAddressOf(), nullptr);
if (FAILED(hr))
return hr;
ComPtr<IDirect3DTexture9> pStagingTexture;
if (pool == D3DPOOL_DEFAULT)
{
hr = device->CreateTexture(iWidth, iHeight, iMipCount,
0u, fmt, D3DPOOL_SYSTEMMEM, pStagingTexture.GetAddressOf(), nullptr);
if (FAILED(hr))
return hr;
}
else
{
pStagingTexture = pTexture;
}
// Lock, fill, unlock
size_t NumBytes = 0;
size_t RowBytes = 0;
size_t NumRows = 0;
const uint8_t* pSrcBits = bitData;
const uint8_t* pEndBits = bitData + bitSize;
D3DLOCKED_RECT LockedRect = {};
for (UINT i = 0; i < iMipCount; ++i)
{
GetSurfaceInfo(iWidth, iHeight, fmt, &NumBytes, &RowBytes, &NumRows);
if (NumBytes > UINT32_MAX || RowBytes > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
if ((pSrcBits + NumBytes) > pEndBits)
{
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
}
if (SUCCEEDED(pStagingTexture->LockRect(i, &LockedRect, nullptr, 0)))
{
auto pDestBits = static_cast<uint8_t*>(LockedRect.pBits);
// Copy stride line by line
for (UINT h = 0; h < NumRows; h++)
{
memcpy_s(pDestBits, static_cast<size_t>(LockedRect.Pitch), pSrcBits, RowBytes);
pDestBits += LockedRect.Pitch;
pSrcBits += RowBytes;
}
pStagingTexture->UnlockRect(i);
}
iWidth = iWidth >> 1;
iHeight = iHeight >> 1;
if (iWidth == 0)
iWidth = 1;
if (iHeight == 0)
iHeight = 1;
}
if (pool == D3DPOOL_DEFAULT)
{
hr = device->UpdateTexture(pStagingTexture.Get(), pTexture.Get());
if (FAILED(hr))
return hr;
}
*texture = pTexture.Detach();
}
return hr;
}