目标
DDS(DirectDraw Surface)是微软为DirectX准备的一种纹理格式。我注意到可以在VisualStudio中预览:
我当前关注这种纹理格式,是因为:
- 不管是D3D11的官方范例(在Microsoft DirectX SDK (June 2010)中),还是D3D12的官方范例(microsoft/DirectX-Graphics-Samples),其中的纹理资源都是DDS格式(DX12龙书中的范例也用DDS)。范例代码直接使用了DDS专用的接口将数据读入并转换为DX资源,这个过程不透明。然而,我在写自己的图形API学习工程时,我需要非DDS格式的纹理。因此,我需要了解DDS被解析并转换为DX资源的细节,以便能将其替换为其他格式。
- 对于立方体纹理(TextureCube),我在VisualStudio中似乎并不能看到所有的6个面,甚至连它是不是立方体纹理都不知道。同样,它转变为DX资源的细节也需要研究。
因此,我决定尝试用底层的方法对DDS文件进行解析,参考代码:
- Microsoft DirectX SDK (June 2010)中的【DDSWithoutD3DX11】
- microsoft/DirectXTex中的DDSTextureLoader11.cpp
我本来的目标是:
- 参考官方代码,一步步地获得一个DDS文件的基本信息(比如是不是立方体纹理),
最后得到原始纹理数据。作为预览测试,我将最后得到的纹理数据创建为bmp图像。
但实际发现,由于牵扯到 压缩(Texture Block Compression in Direct3D 11)还有非8位的像素数据(HDR)。得到纹理的数据并将其转变为bmp是个相对复杂的事情(详细见Programming Guide for DDS - Win32 apps | Microsoft Docs)。因此,最后我只做了解析文件头的工作。
简单分析官方的代码
在Microsoft DirectX SDK (June 2010)中的【DDSWithoutD3DX11】的DDSTextureLoader.h
可以看到:
它对外分别为DX9与DX11提供了一个接口,将DDS文件转变为DX的资源。
内部,这个函数主要分为两部分:
- 使用
LoadTextureDataFromFile
将数据读到pHeader
、pBitData
中 - 使用上一步得到数据作为参数调用
CreateTextureFromDDS
获得DX资源。
在 microsoft/DirectX-Graphics-Samples中的DDSTextureLoader11.cpp中代码已有较大变化,但是主要部分还是这两个:
0. 代码初步结构
仿照官方代码,初步结构如下:
#include <iostream>
#include <Windows.h>
#include "DDS.h"
#include "HRESULTStrings.h"
using namespace DirectX;
//从文件中读取DDS的文件头与图像数据
HRESULT LoadTextureDataFromFile(
const wchar_t* fileName, //dds文件名
BYTE** fileDataBuffer, //容纳文件数据的Buffer指针
const DDS_HEADER** header, //dds文件头
const uint8_t** bitData, //图像数据
size_t* bitSize //图像数据的尺寸
)
{
return S_OK;
}
//根据DDS的数据解析出基本信息
HRESULT LogBasicDataFromDDS(
const DDS_HEADER* header, //dds文件头
const uint8_t* bitData, //图像数据
size_t bitSize, //图像数据的尺寸
const wchar_t* outputPath_noExt //输出的文件名(不带后缀)
)
{
return S_OK;
}
int main()
{
//测试读取的dds文件名
const wchar_t* ddsFileName = L"D:/Temp/uffizi_cross.dds";
//输出的路径
const wchar_t* outputPath = L"D:/Temp/output";
std::wcout << "尝试读取文件:" << ddsFileName << std::endl;
//DDS的数据:
BYTE* fileDataBuffer = NULL; //容纳文件数据的Buffer
const DDS_HEADER* header = nullptr; //指向dds文件头的指针
const uint8_t* bitData = nullptr; //指向图像数据的指针
size_t bitSize = 0; //图像数据的尺寸
//从文件中读取DDS的文件头与图像数据
HRESULT hr = LoadTextureDataFromFile(ddsFileName, &fileDataBuffer, &header, &bitData, &bitSize);
if (hr != S_OK)//读取失败:
{
std::cout << "LoadTextureDataFromFile失败" << std::endl;
//输出hr代表的信息
std::cout << GetHRESULTMessage(hr);
//释放Buffer
if (fileDataBuffer)
delete[] fileDataBuffer;
return 0;
}
//根据DDS的数据解析出基本信息
hr = LogBasicDataFromDDS(header, bitData, bitSize, outputPath);
if (hr != S_OK)//创建失败:
{
std::cout << "LogBasicDataFromDDS失败" << std::endl;
//输出hr代表的信息
std::cout << GetHRESULTMessage(hr);
}
//释放Buffer
if (fileDataBuffer)
delete[] fileDataBuffer;
return 0;
}
其中,DDS.h
来源于官方范例DirectXTex/DirectXTex/DDS.h。(不过需要补充上一个#include <dxgiformat.h>
)
而HRESULTStrings.h
来源于我之前的博客《制作一个小工具:自动生成“获得特定HRESULT对应信息的函数”的代码》。它提供了一个函数GetHRESULTMessage
可以将HRESULT
转变为字符串信息。
1. 从文件中读取DDS的文件头与图像数据
下面深入LoadTextureDataFromFile
函数:
1.1 打开文件
//打开文件
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
HANDLE hFile = (CreateFile2(fileName,
GENERIC_READ,
FILE_SHARE_READ,
OPEN_EXISTING,
nullptr));
#else
HANDLE hFile = (CreateFileW(fileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr));
#endif
if (INVALID_HANDLE_VALUE == hFile) //打开文件失败
return GetLastError();
官方似乎还对Windows版本做了区分,我这边也将其逻辑搬了过来
1.2 获得文件信息并检查文件的尺寸
//获得文件信息
FILE_STANDARD_INFO fileInfo;
if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &fileInfo, sizeof(fileInfo)))
return HRESULT_FROM_WIN32(GetLastError());
//检查文件尺寸
{
//文件太大了,拒绝读取
if (fileInfo.EndOfFile.HighPart > 0)
return E_FAIL;
//文件太小了,一定不是一个有效的DDS文件
//DDS文件开头是一个魔法数(uint32_t)加DDS_HEADER,因此尺寸一定比它大
if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER)))
return E_FAIL;
std::cout << "文件尺寸合格,大小是:" << fileInfo.EndOfFile.LowPart << std::endl;
}
1.3 读取数据
//读取文件中的数据到fileDataBuffer中
{
//创建足够的空间可读取文件数据
*fileDataBuffer = new BYTE[fileInfo.EndOfFile.LowPart];
if (!fileDataBuffer)//这里创建失败是因为超过内存限制
return E_OUTOFMEMORY;
//读取数据
DWORD bytesRead = 0; //读了多少BYTE
if (!ReadFile(hFile, *fileDataBuffer, fileInfo.EndOfFile.LowPart, &bytesRead, nullptr))
{
delete[] fileDataBuffer;
return GetLastError();
}
//如果读取的BYTE数目小于文件大小,则有问题
if (bytesRead < fileInfo.EndOfFile.LowPart)
{
delete[] fileDataBuffer;
return E_FAIL;
}
std::cout << "读取文件数据成功" << std::endl;
}
1.4 检查魔法数字
DDS.h
中定义了一个魔法数字,此数字的值就是4个char:“DDS”加一个空格。
下面检查一下所读文件开头是不是它
//DDS文件一定会以一个魔法数字("DDS ")做开头,检查一下
{
auto dwMagicNumber = *reinterpret_cast<const uint32_t*>(*fileDataBuffer);
if (dwMagicNumber != DDS_MAGIC)
{
delete[] fileDataBuffer;
return E_FAIL;
}
std::cout << "开头确实是“DDS ”" << std::endl;
}
1.5 获得文件头并检查
//文件头的位置是:文件初始位置 + 魔法数字("DDS ")的偏移
auto header_data = reinterpret_cast<const DDS_HEADER*>(*fileDataBuffer + sizeof(uint32_t));
//检查:
{
//验证文件头是否有效
if (header_data->size != sizeof(DDS_HEADER) ||
header_data->ddspf.size != sizeof(DDS_PIXELFORMAT))
{
delete[] fileDataBuffer;
return E_FAIL;
}
std::cout << "通过文件头的 size 和 ddspf.size 判断是一个有效的文件头"<<std::endl;
}
1.6 检查是否有DX10扩展
//检查是否有DX10扩展
bool bDXT10Header = false;
{
//FOURCC: Four-character codes,四个字符的编码
if ((header_data->ddspf.flags & DDS_FOURCC) && //ddspf.flags有DDS_FOURCC说明ddspf.fourCC有效
//检查ddspf.fourCC的值是不是DX10
(MAKEFOURCC('D', 'X', '1', '0') == header_data->ddspf.fourCC))
{
// 现在,文件的大小至少是: 魔法数字 + DDS_HEADER + DDS_HEADER_DXT10 了
if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10)))
{
delete[] fileDataBuffer;
return E_FAIL;
}
bDXT10Header = true;
}
std::cout << "是否有DX10扩展:" << (bDXT10Header ? "true" : "false") << std::endl;
}
首先,代码中的术语 “FOURCC” 指的是:Four-character codes,四字符的编码。
在 DDS_HEADER 中的 DDS_PIXELFORMAT ddspf 的dwFlags
,其中一个flag DDPF_FOURCC
标志着它的fourCC
成员是否包含一个有效的数据
如果包含,则接下来会检查fourCC
的值是不是正好等于'D', 'X', '1', '0'
,如果是则表明它是一个DX10扩展。
1.7 最终返回所有需要的DDS数据
//最终返回所有读取到的DDS数据
{
//文件头
*header = header_data;
//图像数据的偏移
uint32_t bitData_offset =
sizeof(uint32_t) //魔法数字
+ sizeof(DDS_HEADER) //DDS文件头
+ (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0); //可能的DX10扩展
//图像数据指针:
*bitData = *fileDataBuffer + bitData_offset;
//图像数据的尺寸
*bitSize = fileInfo.EndOfFile.LowPart - bitData_offset;
std::cout << "读取成功,得到图像数据的尺寸是:"<< *bitSize << std::endl;
}
2. 根据DDS的数据解析出基本信息
下面深入LogBasicDataFromDDS
函数
2.1 获得图像宽高深
UINT width = header->width; //宽度
UINT height = header->height; //高度
UINT depth = header->depth; //深度
std::cout << "宽度:" << width << "," << "高度:" << height << "," << "深度:" << depth << std::endl;
2.2 检查是否有DX10扩展
接下来的一些逻辑还和是否有DX10扩展有关,所以这里再检查一次
//检查是否有dx10扩展
const DDS_HEADER_DXT10* d3d10ext =
((header->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) ?
reinterpret_cast<const DDS_HEADER_DXT10*>(reinterpret_cast<const uint8_t*>(header) + sizeof(DDS_HEADER))
: nullptr;
std::cout << "是否有DX10扩展:" << (d3d10ext ? "true" : "false") << std::endl;
如果无扩展,则d3d10ext
的值为nullptr
,后续将通过此值来判断。
2.3 获得格式(DXGI_FORMAT)
//获得格式
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
{
if (d3d10ext == nullptr)//如果无DX10扩展
{
//通过文件头的 DDS_PIXELFORMAT ddspf
format = GetDXGIFormat(header->ddspf);
if (format == DXGI_FORMAT_UNKNOWN) //没找到格式
return ERROR_NOT_SUPPORTED;
}
else //有DX10扩展
{
switch (d3d10ext->dxgiFormat)
{
case DXGI_FORMAT_AI44:
case DXGI_FORMAT_IA44:
case DXGI_FORMAT_P8:
case DXGI_FORMAT_A8P8:
return ERROR_NOT_SUPPORTED;
}
//通过dx10扩展上的信息
format = d3d10ext->dxgiFormat;
}
if (BitsPerPixel(format) == 0) //格式不支持
return ERROR_NOT_SUPPORTED;
std::cout << "格式:" << GetEnumString_DXGI_FORMAT(format) << " 位数:" << BitsPerPixel(format) << std::endl;
}
首先需要指出,这里需要两个函数:
DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf)
函数通过一个给定的DDS_PIXELFORMAT
数据判断出实际的DXGI_FORMAT
格式。size_t BitsPerPixel(_In_ DXGI_FORMAT fmt)
函数得到一个DXGI_FORMAT
格式对应有几位(bit)。如果格式不支持则返回0。
这两个函数我都从DirectXTex/DDSTextureLoader/DDSTextureLoader11.cpp中拷贝了过来。
然后,逻辑上分为两部分:
- 对于没有DX10扩展的,格式是通过
GetDXGIFormat
函数从DDS_HEADER 中的 DDS_PIXELFORMAT ddspf 得到。 - 对于有DX10扩展的,则直接是DDS_HEADER_DXT10的
dxgiFormat
。
GetEnumString_DXGI_FORMAT
函数负责将格式转换为能显示的字符串,其得到的方式记录在之前的博客《制作一个小工具:自动生成“将特定枚举值转换成字符串的C++函数”的代码》中。
2.4 得到纹理的种类(普通2D贴图?立方体?3D体数据?)
关于“纹理的种类”,有下面几个变量可以描述:
uint32_t resDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; //资源的维度,例如体数据是三维
UINT arraySize = 1; //纹理有几张,例如立方体纹理是6张
bool isCubeMap = false; //是否是立方体纹理
随后,也根据是否有DX10扩展分为了两种情况:
对于无DX10扩展的情况:
if (header->flags & DDS_HEADER_FLAGS_VOLUME)//查看是否是3D纹理
{
resDim = D3D11_RESOURCE_DIMENSION_TEXTURE3D;
}
else //不是3D纹理
{
//caps2上有cubemap相关的信息
if (header->caps2 & DDS_CUBEMAP)
{
//这里要求必须有六个面
if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES)
return 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
}
首先,它靠DDS_HEADER 的Flags
成员来判断是否是体数据:
随后,它依靠DDS_HEADER 的caps2
成员来判断是否是CubeMap:
而DDS_CUBEMAP_ALLFACES
意味着所有的六个面都在:
对于有DX10扩展的情况:
arraySize = d3d10ext->arraySize;
if (arraySize == 0)
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
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 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 ERROR_INVALID_DATA;
if (arraySize > 1)
return ERROR_NOT_SUPPORTED;
break;
default:
return ERROR_NOT_SUPPORTED;
}
resDim = d3d10ext->resourceDimension;
它依靠DDS_HEADER_DXT10的miscFlag
成员来判断是否是一个CubeMap:
完整代码
#include <iostream>
#include <Windows.h>
#include<d3d11.h>
#include "DDS.h"
#include "DDSHelper.h"
#include "HRESULTStrings.h"
#include "GetEnumString_DXGI_FORMAT.h"
#include "GetEnumString_D3D11_RESOURCE_DIMENSION.h"
using namespace DirectX;
//从文件中读取DDS的文件头与图像数据
HRESULT LoadTextureDataFromFile(
const wchar_t* fileName, //dds文件名
BYTE** fileDataBuffer, //容纳文件数据的Buffer指针
const DDS_HEADER** header, //dds文件头
const uint8_t** bitData, //图像数据
size_t* bitSize //图像数据的尺寸
)
{
//打开文件
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
HANDLE hFile = (CreateFile2(fileName,
GENERIC_READ,
FILE_SHARE_READ,
OPEN_EXISTING,
nullptr));
#else
HANDLE hFile = (CreateFileW(fileName,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr));
#endif
if (INVALID_HANDLE_VALUE == hFile) //打开文件失败
return GetLastError();
//获得文件信息
FILE_STANDARD_INFO fileInfo;
if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &fileInfo, sizeof(fileInfo)))
return HRESULT_FROM_WIN32(GetLastError());
//检查文件尺寸
{
//文件太大了,拒绝读取
if (fileInfo.EndOfFile.HighPart > 0)
return E_FAIL;
//文件太小了,一定不是一个有效的DDS文件
//DDS文件开头是一个魔法数(uint32_t)加DDS_HEADER,因此尺寸一定比它大
if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER)))
return E_FAIL;
std::cout << "文件尺寸合格,大小是:" << fileInfo.EndOfFile.LowPart << std::endl;
}
//读取文件中的数据到fileDataBuffer中
{
//创建足够的空间可读取文件数据
*fileDataBuffer = new BYTE[fileInfo.EndOfFile.LowPart];
if (!fileDataBuffer)//这里创建失败是因为超过内存限制
return E_OUTOFMEMORY;
//读取数据
DWORD bytesRead = 0; //读了多少BYTE
if (!ReadFile(hFile, *fileDataBuffer, fileInfo.EndOfFile.LowPart, &bytesRead, nullptr))
{
delete[] fileDataBuffer;
return GetLastError();
}
//如果读取的BYTE数目小于文件大小,则有问题
if (bytesRead < fileInfo.EndOfFile.LowPart)
{
delete[] fileDataBuffer;
return E_FAIL;
}
std::cout << "读取文件数据成功" << std::endl;
}
//DDS文件一定会以一个魔法数字("DDS ")做开头,检查一下
{
auto dwMagicNumber = *reinterpret_cast<const uint32_t*>(*fileDataBuffer);
if (dwMagicNumber != DDS_MAGIC)
{
delete[] fileDataBuffer;
return E_FAIL;
}
std::cout << "开头确实是“DDS ”" << std::endl;
}
//文件头的位置是:文件初始位置 + 魔法数字("DDS ")的偏移
auto header_data = reinterpret_cast<const DDS_HEADER*>(*fileDataBuffer + sizeof(uint32_t));
//检查:
{
//验证文件头是否有效
if (header_data->size != sizeof(DDS_HEADER) ||
header_data->ddspf.size != sizeof(DDS_PIXELFORMAT))
{
delete[] fileDataBuffer;
return E_FAIL;
}
std::cout << "通过文件头的 size 和 ddspf.size 判断是一个有效的文件头"<<std::endl;
}
//检查是否有DX10扩展
bool bDXT10Header = false;
{
//FOURCC: Four-character codes,四个字符的编码
if ((header_data->ddspf.flags & DDS_FOURCC) && //ddspf.flags有DDS_FOURCC说明ddspf.fourCC有效
//检查ddspf.fourCC的值是不是DX10
(MAKEFOURCC('D', 'X', '1', '0') == header_data->ddspf.fourCC))
{
// 现在,文件的大小至少是: 魔法数字 + DDS_HEADER + DDS_HEADER_DXT10 了
if (fileInfo.EndOfFile.LowPart < (sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10)))
{
delete[] fileDataBuffer;
return E_FAIL;
}
bDXT10Header = true;
}
std::cout << "是否有DX10扩展:" << (bDXT10Header ? "true" : "false") << std::endl;
}
//最终返回所有读取到的DDS数据
{
//文件头
*header = header_data;
//图像数据的偏移
uint32_t bitData_offset =
sizeof(uint32_t) //魔法数字
+ sizeof(DDS_HEADER) //DDS文件头
+ (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0); //可能的DX10扩展
//图像数据指针:
*bitData = *fileDataBuffer + bitData_offset;
//图像数据的尺寸
*bitSize = fileInfo.EndOfFile.LowPart - bitData_offset;
std::cout << "读取成功,得到图像数据的尺寸是:"<< *bitSize << std::endl;
}
return S_OK;
}
//根据DDS的数据解析出基本信息
HRESULT LogBasicDataFromDDS(
const DDS_HEADER* header, //dds文件头
const uint8_t* bitData, //图像数据
size_t bitSize, //图像数据的尺寸
const wchar_t* outputPath_noExt //输出的文件名(不带后缀)
)
{
UINT width = header->width; //宽度
UINT height = header->height; //高度
UINT depth = header->depth; //深度
std::cout << "宽度:" << width << "," << "高度:" << height << "," << "深度:" << depth << std::endl;
//检查是否有dx10扩展
const DDS_HEADER_DXT10* d3d10ext =
((header->ddspf.flags & DDS_FOURCC) && (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) ?
reinterpret_cast<const DDS_HEADER_DXT10*>(reinterpret_cast<const uint8_t*>(header) + sizeof(DDS_HEADER))
: nullptr;
std::cout << "是否有DX10扩展:" << (d3d10ext ? "true" : "false") << std::endl;
//获得格式
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
{
if (d3d10ext == nullptr)//如果无DX10扩展
{
//通过文件头的 DDS_PIXELFORMAT ddspf
format = GetDXGIFormat(header->ddspf);
if (format == DXGI_FORMAT_UNKNOWN) //没找到格式
return ERROR_NOT_SUPPORTED;
}
else //有DX10扩展
{
switch (d3d10ext->dxgiFormat)
{
case DXGI_FORMAT_AI44:
case DXGI_FORMAT_IA44:
case DXGI_FORMAT_P8:
case DXGI_FORMAT_A8P8:
return ERROR_NOT_SUPPORTED;
}
//通过dx10扩展上的信息
format = d3d10ext->dxgiFormat;
}
if (BitsPerPixel(format) == 0) //格式不支持
return ERROR_NOT_SUPPORTED;
std::cout << "格式:" << GetEnumString_DXGI_FORMAT(format) << " 位数:" << BitsPerPixel(format) << std::endl;
}
//得到纹理的种类
uint32_t resDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; //资源的维度,例如体数据是三维
UINT arraySize = 1; //纹理有几张,例如立方体纹理是6张
bool isCubeMap = false; //是否是立方体纹理
{
if (d3d10ext == nullptr)//如果无DX10扩展
{
if (header->flags & DDS_HEADER_FLAGS_VOLUME)//查看是否是3D纹理
{
resDim = D3D11_RESOURCE_DIMENSION_TEXTURE3D;
}
else //不是3D纹理
{
//caps2上有cubemap相关的信息
if (header->caps2 & DDS_CUBEMAP)
{
//这里要求必须有六个面
if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES)
return 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
}
}
else //有DX10扩展
{
arraySize = d3d10ext->arraySize;
if (arraySize == 0)
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
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 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 ERROR_INVALID_DATA;
if (arraySize > 1)
return ERROR_NOT_SUPPORTED;
break;
default:
return ERROR_NOT_SUPPORTED;
}
resDim = d3d10ext->resourceDimension;
}
std::cout << "资源的维度:" << GetEnumString_D3D11_RESOURCE_DIMENSION((D3D11_RESOURCE_DIMENSION)resDim) << std::endl;
std::cout << "纹理有几张:" << arraySize << std::endl;
std::cout << "是否是CubeMap:" << (isCubeMap ? "true" : "false")<<std::endl;
}
return S_OK;
}
int main()
{
//测试读取的dds文件名
const wchar_t* ddsFileName = L"D:/Temp/uffizi_cross.dds";
//输出的路径
const wchar_t* outputPath = L"D:/Temp/output";
std::wcout << "尝试读取文件:" << ddsFileName << std::endl;
//DDS的数据:
BYTE* fileDataBuffer = NULL; //容纳文件数据的Buffer
const DDS_HEADER* header = nullptr; //指向dds文件头的指针
const uint8_t* bitData = nullptr; //指向图像数据的指针
size_t bitSize = 0; //图像数据的尺寸
//根据DDS的数据解析出基本信息
HRESULT hr = LoadTextureDataFromFile(ddsFileName, &fileDataBuffer, &header, &bitData, &bitSize);
if (hr != S_OK)//读取失败:
{
std::cout << "LoadTextureDataFromFile失败" << std::endl;
//输出hr代表的信息
std::cout << GetHRESULTMessage(hr);
//释放Buffer
if (fileDataBuffer)
delete[] fileDataBuffer;
return 0;
}
//根据DDS的数据创建Bmp图片
hr = LogBasicDataFromDDS(header, bitData, bitSize, outputPath);
if (hr != S_OK)//创建失败:
{
std::cout << "CreateBmpTextureFromDDS失败" << std::endl;
//输出hr代表的信息
std::cout << GetHRESULTMessage(hr);
}
//释放Buffer
if (fileDataBuffer)
delete[] fileDataBuffer;
return 0;
}
测试
尝试读取Microsoft DirectX SDK (June 2010)中的:
- 【DDSWithoutD3DX11】的
seafloor.dds
- 【HDRFormats】的
uffizi_cross.dds
尝试读取文件:D:/Temp/seafloor.dds
文件尺寸合格,大小是:43832
读取文件数据成功
开头确实是“DDS ”
通过文件头的 size 和 ddspf.size 判断是一个有效的文件头
是否有DX10扩展:false
读取成功,得到图像数据的尺寸是:43704
宽度:256,高度:256,深度:0
是否有DX10扩展:false
格式:DXGI_FORMAT_BC1_UNORM 位数:4
资源的维度:D3D11_RESOURCE_DIMENSION_TEXTURE2D
纹理有几张:1
是否是CubeMap:false
尝试读取文件:D:/Temp/uffizi_cross.dds
文件尺寸合格,大小是:12583040
读取文件数据成功
开头确实是“DDS ”
通过文件头的 size 和 ddspf.size 判断是一个有效的文件头
是否有DX10扩展:false
读取成功,得到图像数据的尺寸是:12582912
宽度:512,高度:512,深度:0
是否有DX10扩展:false
格式:DXGI_FORMAT_R16G16B16A16_FLOAT 位数:64
资源的维度:D3D11_RESOURCE_DIMENSION_TEXTURE2D
纹理有几张:6
是否是CubeMap:true