基于DirectXTex实现DDS格式转换函数的问题求助
DDS格式转换函数修正方案
原代码核心问题
- COM资源泄漏:调用
CoInitializeEx后未调用CoUninitialize,会导致COM引用计数异常 - Alpha通道处理错误:转换到单通道格式(如
DXGI_FORMAT_R8_UNORM)时,未保留Alpha通道的透明度信息,导致透明背景被填充为白色 - 解压参数不当:使用
DXGI_FORMAT_UNKNOWN作为解压目标格式,可能让DirectXTex选择丢失Alpha的中间格式 - 过滤器标志滥用:错误组合多个默认过滤器标志,可能触发不必要的颜色空间转换或滤波逻辑
- 冗余平面格式处理:DirectXTex的
Convert/Compress函数原生支持平面格式,无需手动转单平面
修正后的完整实现
#include <vector> #include <DirectXTex.h> #include <stdexcept> #include <utility> // 假设项目中已有typedef:typedef unsigned char byte; // 若无此定义,可替换为unsigned char void throw_error(const std::string& err, HRESULT hr) { LPWSTR errorText = nullptr; const DWORD messageLength = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, hr & 0x0000FFFF, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&errorText), 0, nullptr); if (messageLength > 0) { const std::wstring wErrorText(errorText); LocalFree(errorText); throw std::invalid_argument(err + ": " + std::string(wErrorText.begin(), wErrorText.end())); } else { throw std::invalid_argument(err + ": Unknown HRESULT error"); } } std::vector<byte> ConvertDDS(const byte* src, size_t src_len, DXGI_FORMAT targetFormat) { HRESULT hr = S_OK; // COM初始化:用RAII确保退出时自动清理 hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (FAILED(hr)) throw_error("Failed to initialize COM", hr); std::unique_ptr<void, decltype(&CoUninitialize)> comGuard(nullptr, [](void*) { CoUninitialize(); }); DirectX::TexMetadata srcMeta; DirectX::ScratchImage srcImage; // 加载输入DDS hr = DirectX::LoadFromDDSMemory(src, src_len, DirectX::DDS_FLAGS_ALLOW_LARGE_FILES, &srcMeta, srcImage); if (FAILED(hr)) throw_error("Failed to load DDS from memory", hr); DirectX::ScratchImage dstImage; // 格式已匹配,直接复用原图像 if (srcMeta.format == targetFormat) { dstImage = std::move(srcImage); } else { DirectX::ScratchImage tempImage; const DirectX::TexMetadata* currentMeta = &srcMeta; const DirectX::Image* currentImages = srcImage.GetImages(); size_t currentImageCount = srcImage.GetImageCount(); // 处理压缩格式:解压到带Alpha的中间格式,保留透明度信息 if (DirectX::IsCompressed(srcMeta.format)) { DXGI_FORMAT decompressFormat = DirectX::IsSRGB(srcMeta.format) ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM; hr = DirectX::Decompress(currentImages, currentImageCount, *currentMeta, decompressFormat, tempImage); if (FAILED(hr)) throw_error("Failed to decompress DDS", hr); currentImages = tempImage.GetImages(); currentImageCount = tempImage.GetImageCount(); currentMeta = &tempImage.GetMetadata(); } // 执行格式转换:针对单通道目标格式,处理Alpha通道 DirectX::TEX_FILTER_FLAGS filterFlags = DirectX::TEX_FILTER_DEFAULT; // 若目标是R8_UNORM,可选择提取Alpha作为灰度值,解决透明背景变白问题 if (targetFormat == DXGI_FORMAT_R8_UNORM) { filterFlags |= DirectX::TEX_FILTER_EXTRACT_ALPHA; // 若需要将RGB混合Alpha后转灰度,移除上述标志即可 } hr = DirectX::Convert(currentImages, currentImageCount, *currentMeta, targetFormat, filterFlags, DirectX::TEX_THRESHOLD_DEFAULT, dstImage); if (FAILED(hr)) throw_error("Failed to convert DDS format", hr); // 目标格式为压缩格式时,重新压缩 if (DirectX::IsCompressed(targetFormat)) { DirectX::ScratchImage compressedImage; hr = DirectX::Compress(dstImage.GetImages(), dstImage.GetImageCount(), dstImage.GetMetadata(), targetFormat, DirectX::TEX_COMPRESS_DEFAULT, DirectX::TEX_THRESHOLD_DEFAULT, compressedImage); if (FAILED(hr)) throw_error("Failed to compress DDS", hr); dstImage = std::move(compressedImage); } } // 保存转换后的DDS到内存 DirectX::Blob ddsBlob; hr = DirectX::SaveToDDSMemory(dstImage.GetImages(), dstImage.GetImageCount(), dstImage.GetMetadata(), DirectX::DDS_FLAGS_NONE, ddsBlob); if (FAILED(hr)) throw_error("Failed to save DDS to memory", hr); // 转换为vector返回 std::vector<byte> result(static_cast<size_t>(ddsBlob.GetBufferSize())); std::memcpy(result.data(), ddsBlob.GetBufferPointer(), result.size()); return result; }
关键修改说明
- COM资源RAII管理:用
std::unique_ptr做资源守卫,确保函数退出时自动调用CoUninitialize,避免资源泄漏 - Alpha通道处理:针对
DXGI_FORMAT_R8_UNORM这类单通道格式,通过TEX_FILTER_EXTRACT_ALPHA标志直接提取Alpha通道作为灰度值,解决透明背景变白的问题;若需要将RGB混合Alpha后转灰度,移除该标志即可 - 明确解压格式:不再使用
DXGI_FORMAT_UNKNOWN,而是指定带Alpha的中间格式,确保解压过程中保留透明度信息 - 简化转换流程:移除冗余的平面格式转换逻辑,DirectXTex原生支持多数平面格式的转换
- 优化过滤器标志:仅保留必要标志位,避免错误触发颜色空间转换或不必要的滤波
- 内存优化:用
std::move转移ScratchImage所有权,减少内存拷贝
内容的提问来源于stack exchange,提问作者code-tester
相关产品推荐
相关产品推荐

