aboutsummaryrefslogtreecommitdiff
path: root/libs/preview_dds_native/src/ddsformat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/preview_dds_native/src/ddsformat.cpp')
-rw-r--r--libs/preview_dds_native/src/ddsformat.cpp619
1 files changed, 619 insertions, 0 deletions
diff --git a/libs/preview_dds_native/src/ddsformat.cpp b/libs/preview_dds_native/src/ddsformat.cpp
new file mode 100644
index 0000000..b56db91
--- /dev/null
+++ b/libs/preview_dds_native/src/ddsformat.cpp
@@ -0,0 +1,619 @@
+#include "ddsformat.h"
+
+#include <QOpenGLFunctions>
+#include <QString>
+
+#include <algorithm>
+#include <cstring>
+
+// GL compressed format constants not always in headers
+#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#endif
+#ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#endif
+#ifndef GL_COMPRESSED_RED_RGTC1
+#define GL_COMPRESSED_RED_RGTC1 0x8DBB
+#endif
+#ifndef GL_COMPRESSED_SIGNED_RED_RGTC1
+#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
+#endif
+#ifndef GL_COMPRESSED_RG_RGTC2
+#define GL_COMPRESSED_RG_RGTC2 0x8DBD
+#endif
+#ifndef GL_COMPRESSED_SIGNED_RG_RGTC2
+#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
+#endif
+#ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT
+#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
+#endif
+#ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT
+#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
+#endif
+#ifndef GL_COMPRESSED_RGBA_BPTC_UNORM
+#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM
+#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D
+#endif
+#ifndef GL_COMPRESSED_SRGB_S3TC_DXT1_EXT
+#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
+#endif
+#ifndef GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
+#endif
+
+static GLFormatInfo dxgiToGL(DXGIFormat fmt)
+{
+ GLFormatInfo info;
+ info.valid = true;
+
+ switch (fmt) {
+ // BC1 (DXT1)
+ case DXGIFormat::BC1_UNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ break;
+ case DXGIFormat::BC1_UNORM_SRGB:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
+ break;
+ // BC2 (DXT3)
+ case DXGIFormat::BC2_UNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ break;
+ case DXGIFormat::BC2_UNORM_SRGB:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
+ break;
+ // BC3 (DXT5)
+ case DXGIFormat::BC3_UNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ break;
+ case DXGIFormat::BC3_UNORM_SRGB:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
+ break;
+ // BC4
+ case DXGIFormat::BC4_UNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RED_RGTC1;
+ break;
+ case DXGIFormat::BC4_SNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_SIGNED_RED_RGTC1;
+ break;
+ // BC5
+ case DXGIFormat::BC5_UNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RG_RGTC2;
+ break;
+ case DXGIFormat::BC5_SNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_SIGNED_RG_RGTC2;
+ break;
+ // BC6H
+ case DXGIFormat::BC6H_UF16:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
+ break;
+ case DXGIFormat::BC6H_SF16:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
+ break;
+ // BC7
+ case DXGIFormat::BC7_UNORM:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM;
+ break;
+ case DXGIFormat::BC7_UNORM_SRGB:
+ info.compressed = true;
+ info.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
+ break;
+
+ // Uncompressed RGBA
+ case DXGIFormat::R8G8B8A8_UNORM:
+ case DXGIFormat::R8G8B8A8_UNORM_SRGB:
+ info.internalFormat = GL_RGBA8;
+ info.format = GL_RGBA;
+ info.type = GL_UNSIGNED_BYTE;
+ break;
+ case DXGIFormat::R8G8B8A8_UINT:
+ info.internalFormat = GL_RGBA8UI;
+ info.format = GL_RGBA_INTEGER;
+ info.type = GL_UNSIGNED_BYTE;
+ info.sampler = SamplerType::UInt;
+ break;
+ case DXGIFormat::R8G8B8A8_SNORM:
+ info.internalFormat = GL_RGBA8_SNORM;
+ info.format = GL_RGBA;
+ info.type = GL_BYTE;
+ break;
+ case DXGIFormat::R8G8B8A8_SINT:
+ info.internalFormat = GL_RGBA8I;
+ info.format = GL_RGBA_INTEGER;
+ info.type = GL_BYTE;
+ info.sampler = SamplerType::SInt;
+ break;
+
+ // BGRA
+ case DXGIFormat::B8G8R8A8_UNORM:
+ case DXGIFormat::B8G8R8A8_UNORM_SRGB:
+ info.internalFormat = GL_RGBA8;
+ info.format = GL_BGRA;
+ info.type = GL_UNSIGNED_BYTE;
+ break;
+ case DXGIFormat::B8G8R8X8_UNORM:
+ case DXGIFormat::B8G8R8X8_UNORM_SRGB:
+ info.internalFormat = GL_RGBA8;
+ info.format = GL_BGRA;
+ info.type = GL_UNSIGNED_BYTE;
+ break;
+
+ // 16-bit float
+ case DXGIFormat::R16G16B16A16_FLOAT:
+ info.internalFormat = GL_RGBA16F;
+ info.format = GL_RGBA;
+ info.type = GL_HALF_FLOAT;
+ break;
+ case DXGIFormat::R16G16B16A16_UNORM:
+ info.internalFormat = GL_RGBA16;
+ info.format = GL_RGBA;
+ info.type = GL_UNSIGNED_SHORT;
+ break;
+ case DXGIFormat::R16G16B16A16_UINT:
+ info.internalFormat = GL_RGBA16UI;
+ info.format = GL_RGBA_INTEGER;
+ info.type = GL_UNSIGNED_SHORT;
+ info.sampler = SamplerType::UInt;
+ break;
+ case DXGIFormat::R16G16B16A16_SNORM:
+ info.internalFormat = GL_RGBA16_SNORM;
+ info.format = GL_RGBA;
+ info.type = GL_SHORT;
+ break;
+ case DXGIFormat::R16G16B16A16_SINT:
+ info.internalFormat = GL_RGBA16I;
+ info.format = GL_RGBA_INTEGER;
+ info.type = GL_SHORT;
+ info.sampler = SamplerType::SInt;
+ break;
+
+ // 32-bit float
+ case DXGIFormat::R32G32B32A32_FLOAT:
+ info.internalFormat = GL_RGBA32F;
+ info.format = GL_RGBA;
+ info.type = GL_FLOAT;
+ break;
+ case DXGIFormat::R32G32B32_FLOAT:
+ info.internalFormat = GL_RGB32F;
+ info.format = GL_RGB;
+ info.type = GL_FLOAT;
+ break;
+
+ // RG formats
+ case DXGIFormat::R8G8_UNORM:
+ info.internalFormat = GL_RG8;
+ info.format = GL_RG;
+ info.type = GL_UNSIGNED_BYTE;
+ break;
+ case DXGIFormat::R16G16_FLOAT:
+ info.internalFormat = GL_RG16F;
+ info.format = GL_RG;
+ info.type = GL_HALF_FLOAT;
+ break;
+ case DXGIFormat::R16G16_UNORM:
+ info.internalFormat = GL_RG16;
+ info.format = GL_RG;
+ info.type = GL_UNSIGNED_SHORT;
+ break;
+ case DXGIFormat::R32G32_FLOAT:
+ info.internalFormat = GL_RG32F;
+ info.format = GL_RG;
+ info.type = GL_FLOAT;
+ break;
+
+ // R formats
+ case DXGIFormat::R8_UNORM:
+ info.internalFormat = GL_R8;
+ info.format = GL_RED;
+ info.type = GL_UNSIGNED_BYTE;
+ break;
+ case DXGIFormat::R16_FLOAT:
+ info.internalFormat = GL_R16F;
+ info.format = GL_RED;
+ info.type = GL_HALF_FLOAT;
+ break;
+ case DXGIFormat::R16_UNORM:
+ info.internalFormat = GL_R16;
+ info.format = GL_RED;
+ info.type = GL_UNSIGNED_SHORT;
+ break;
+ case DXGIFormat::R32_FLOAT:
+ info.internalFormat = GL_R32F;
+ info.format = GL_RED;
+ info.type = GL_FLOAT;
+ break;
+ case DXGIFormat::A8_UNORM:
+ info.internalFormat = GL_R8;
+ info.format = GL_RED;
+ info.type = GL_UNSIGNED_BYTE;
+ break;
+
+ // Packed formats
+ case DXGIFormat::R10G10B10A2_UNORM:
+ info.internalFormat = GL_RGB10_A2;
+ info.format = GL_RGBA;
+ info.type = GL_UNSIGNED_INT_2_10_10_10_REV;
+ break;
+ case DXGIFormat::R11G11B10_FLOAT:
+ info.internalFormat = GL_R11F_G11F_B10F;
+ info.format = GL_RGB;
+ info.type = GL_UNSIGNED_INT_10F_11F_11F_REV;
+ break;
+ case DXGIFormat::B5G6R5_UNORM:
+ info.internalFormat = GL_RGB565;
+ info.format = GL_RGB;
+ info.type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case DXGIFormat::B5G5R5A1_UNORM:
+ info.internalFormat = GL_RGB5_A1;
+ info.format = GL_BGRA;
+ info.type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
+ break;
+ case DXGIFormat::B4G4R4A4_UNORM:
+ info.internalFormat = GL_RGBA4;
+ info.format = GL_BGRA;
+ info.type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
+ break;
+
+ default:
+ info.valid = false;
+ break;
+ }
+
+ return info;
+}
+
+DXGIFormat fourCCToDXGI(uint32_t fourCC)
+{
+ if (fourCC == makeFourCC('D', 'X', 'T', '1'))
+ return DXGIFormat::BC1_UNORM;
+ if (fourCC == makeFourCC('D', 'X', 'T', '3'))
+ return DXGIFormat::BC2_UNORM;
+ if (fourCC == makeFourCC('D', 'X', 'T', '5'))
+ return DXGIFormat::BC3_UNORM;
+ if (fourCC == makeFourCC('B', 'C', '4', 'U') ||
+ fourCC == makeFourCC('A', 'T', 'I', '1'))
+ return DXGIFormat::BC4_UNORM;
+ if (fourCC == makeFourCC('B', 'C', '4', 'S'))
+ return DXGIFormat::BC4_SNORM;
+ if (fourCC == makeFourCC('A', 'T', 'I', '2') ||
+ fourCC == makeFourCC('B', 'C', '5', 'U'))
+ return DXGIFormat::BC5_UNORM;
+ if (fourCC == makeFourCC('B', 'C', '5', 'S'))
+ return DXGIFormat::BC5_SNORM;
+
+ // Numeric FourCC codes for float/half-float formats
+ switch (fourCC) {
+ case 36:
+ return DXGIFormat::R16G16B16A16_UNORM;
+ case 110:
+ return DXGIFormat::R16G16B16A16_SNORM;
+ case 111:
+ return DXGIFormat::R16_FLOAT;
+ case 112:
+ return DXGIFormat::R16G16_FLOAT;
+ case 113:
+ return DXGIFormat::R16G16B16A16_FLOAT;
+ case 114:
+ return DXGIFormat::R32_FLOAT;
+ case 115:
+ return DXGIFormat::R32G32_FLOAT;
+ case 116:
+ return DXGIFormat::R32G32B32A32_FLOAT;
+ default:
+ return DXGIFormat::UNKNOWN;
+ }
+}
+
+// Helper: count trailing zeros / bit shift for a mask
+static int maskShift(uint32_t mask)
+{
+ if (mask == 0)
+ return 0;
+ int shift = 0;
+ while ((mask & 1) == 0) {
+ mask >>= 1;
+ ++shift;
+ }
+ return shift;
+}
+
+static int maskBits(uint32_t mask)
+{
+ int count = 0;
+ while (mask) {
+ count += mask & 1;
+ mask >>= 1;
+ }
+ return count;
+}
+
+// Build a converter for arbitrary bitmask pixel formats
+static GLFormatInfo buildBitmaskFormat(const DDSPixelFormat& pf)
+{
+ GLFormatInfo info;
+ info.valid = true;
+
+ int rShift = maskShift(pf.dwRBitMask);
+ int gShift = maskShift(pf.dwGBitMask);
+ int bShift = maskShift(pf.dwBBitMask);
+ int aShift = maskShift(pf.dwABitMask);
+ int rBits = maskBits(pf.dwRBitMask);
+ int gBits = maskBits(pf.dwGBitMask);
+ int bBits = maskBits(pf.dwBBitMask);
+ int aBits = maskBits(pf.dwABitMask);
+ int bpp = pf.dwRGBBitCount;
+
+ // Try to match common uncompressed formats directly
+ if (bpp == 32 && pf.dwRBitMask == 0x000000FF && pf.dwGBitMask == 0x0000FF00 &&
+ pf.dwBBitMask == 0x00FF0000 &&
+ (pf.dwABitMask == 0xFF000000 || pf.dwABitMask == 0)) {
+ info.internalFormat = GL_RGBA8;
+ info.format = GL_RGBA;
+ info.type = GL_UNSIGNED_BYTE;
+ return info;
+ }
+ if (bpp == 32 && pf.dwRBitMask == 0x00FF0000 && pf.dwGBitMask == 0x0000FF00 &&
+ pf.dwBBitMask == 0x000000FF) {
+ info.internalFormat = GL_RGBA8;
+ info.format = GL_BGRA;
+ info.type = GL_UNSIGNED_BYTE;
+ return info;
+ }
+
+ // Generic converter: extract channels and pack into RGBA8
+ uint32_t rMask = pf.dwRBitMask;
+ uint32_t gMask = pf.dwGBitMask;
+ uint32_t bMask = pf.dwBBitMask;
+ uint32_t aMask = pf.dwABitMask;
+ bool hasAlpha = (pf.dwFlags & DDPF_ALPHAPIXELS) && aMask != 0;
+ int bytesPerPixel = bpp / 8;
+
+ info.internalFormat = GL_RGBA8;
+ info.format = GL_RGBA;
+ info.type = GL_UNSIGNED_BYTE;
+ info.converter = [=](const QByteArray& data, int w, int h) -> QByteArray {
+ QByteArray result(w * h * 4, '\0');
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(data.constData());
+ uint8_t* dst = reinterpret_cast<uint8_t*>(result.data());
+
+ for (int i = 0; i < w * h; ++i) {
+ uint32_t pixel = 0;
+ std::memcpy(&pixel, src + i * bytesPerPixel,
+ std::min(bytesPerPixel, 4));
+
+ int r = rBits > 0 ? ((pixel & rMask) >> rShift) * 255 / ((1 << rBits) - 1) : 0;
+ int g = gBits > 0 ? ((pixel & gMask) >> gShift) * 255 / ((1 << gBits) - 1) : 0;
+ int b = bBits > 0 ? ((pixel & bMask) >> bShift) * 255 / ((1 << bBits) - 1) : 0;
+ int a = hasAlpha && aBits > 0
+ ? ((pixel & aMask) >> aShift) * 255 / ((1 << aBits) - 1)
+ : 255;
+
+ dst[i * 4 + 0] = static_cast<uint8_t>(r);
+ dst[i * 4 + 1] = static_cast<uint8_t>(g);
+ dst[i * 4 + 2] = static_cast<uint8_t>(b);
+ dst[i * 4 + 3] = static_cast<uint8_t>(a);
+ }
+ return result;
+ };
+
+ return info;
+}
+
+GLFormatInfo getGLFormat(const DDSPixelFormat& pf, const DDSHeaderDXT10* dxt10)
+{
+ // DX10 extended header takes priority
+ if (dxt10) {
+ return dxgiToGL(dxt10->dxgiFormat);
+ }
+
+ // FourCC compressed or float formats
+ if (pf.dwFlags & DDPF_FOURCC) {
+ DXGIFormat dxgi = fourCCToDXGI(pf.dwFourCC);
+ if (dxgi != DXGIFormat::UNKNOWN) {
+ return dxgiToGL(dxgi);
+ }
+ GLFormatInfo info;
+ info.valid = false;
+ return info;
+ }
+
+ // Uncompressed with bitmasks
+ if (pf.dwFlags & (DDPF_RGB | DDPF_LUMINANCE | DDPF_YUV | DDPF_ALPHA)) {
+ return buildBitmaskFormat(pf);
+ }
+
+ GLFormatInfo info;
+ info.valid = false;
+ return info;
+}
+
+static bool isBlockCompressed(DXGIFormat fmt)
+{
+ switch (fmt) {
+ case DXGIFormat::BC1_UNORM:
+ case DXGIFormat::BC1_UNORM_SRGB:
+ case DXGIFormat::BC2_UNORM:
+ case DXGIFormat::BC2_UNORM_SRGB:
+ case DXGIFormat::BC3_UNORM:
+ case DXGIFormat::BC3_UNORM_SRGB:
+ case DXGIFormat::BC4_UNORM:
+ case DXGIFormat::BC4_SNORM:
+ case DXGIFormat::BC5_UNORM:
+ case DXGIFormat::BC5_SNORM:
+ case DXGIFormat::BC6H_UF16:
+ case DXGIFormat::BC6H_SF16:
+ case DXGIFormat::BC7_UNORM:
+ case DXGIFormat::BC7_UNORM_SRGB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int blockSize(DXGIFormat fmt)
+{
+ switch (fmt) {
+ case DXGIFormat::BC1_UNORM:
+ case DXGIFormat::BC1_UNORM_SRGB:
+ case DXGIFormat::BC4_UNORM:
+ case DXGIFormat::BC4_SNORM:
+ return 8;
+ default:
+ return 16;
+ }
+}
+
+uint32_t mipDataSize(DXGIFormat fmt, const DDSPixelFormat& pf,
+ uint32_t width, uint32_t height)
+{
+ if (isBlockCompressed(fmt)) {
+ uint32_t blocksW = std::max(1u, (width + 3) / 4);
+ uint32_t blocksH = std::max(1u, (height + 3) / 4);
+ return blocksW * blocksH * blockSize(fmt);
+ }
+
+ // Uncompressed: use bits per pixel
+ uint32_t bpp = pf.dwRGBBitCount;
+ if (bpp == 0) {
+ // Estimate from DXGI format for non-bitmask formats
+ switch (fmt) {
+ case DXGIFormat::R32G32B32A32_FLOAT:
+ bpp = 128;
+ break;
+ case DXGIFormat::R32G32B32_FLOAT:
+ bpp = 96;
+ break;
+ case DXGIFormat::R16G16B16A16_FLOAT:
+ case DXGIFormat::R16G16B16A16_UNORM:
+ case DXGIFormat::R16G16B16A16_SNORM:
+ case DXGIFormat::R32G32_FLOAT:
+ bpp = 64;
+ break;
+ case DXGIFormat::R8G8B8A8_UNORM:
+ case DXGIFormat::R8G8B8A8_UNORM_SRGB:
+ case DXGIFormat::B8G8R8A8_UNORM:
+ case DXGIFormat::B8G8R8X8_UNORM:
+ case DXGIFormat::R16G16_FLOAT:
+ case DXGIFormat::R16G16_UNORM:
+ case DXGIFormat::R32_FLOAT:
+ case DXGIFormat::R10G10B10A2_UNORM:
+ case DXGIFormat::R11G11B10_FLOAT:
+ bpp = 32;
+ break;
+ case DXGIFormat::R8G8_UNORM:
+ case DXGIFormat::R16_FLOAT:
+ case DXGIFormat::R16_UNORM:
+ case DXGIFormat::B5G6R5_UNORM:
+ case DXGIFormat::B5G5R5A1_UNORM:
+ case DXGIFormat::B4G4R4A4_UNORM:
+ bpp = 16;
+ break;
+ case DXGIFormat::R8_UNORM:
+ case DXGIFormat::A8_UNORM:
+ bpp = 8;
+ break;
+ default:
+ bpp = 32;
+ break;
+ }
+ }
+ return width * height * bpp / 8;
+}
+
+static const char* dxgiFormatName(DXGIFormat fmt)
+{
+ switch (fmt) {
+ case DXGIFormat::BC1_UNORM:
+ return "BC1_UNORM (DXT1)";
+ case DXGIFormat::BC1_UNORM_SRGB:
+ return "BC1_UNORM_SRGB";
+ case DXGIFormat::BC2_UNORM:
+ return "BC2_UNORM (DXT3)";
+ case DXGIFormat::BC2_UNORM_SRGB:
+ return "BC2_UNORM_SRGB";
+ case DXGIFormat::BC3_UNORM:
+ return "BC3_UNORM (DXT5)";
+ case DXGIFormat::BC3_UNORM_SRGB:
+ return "BC3_UNORM_SRGB";
+ case DXGIFormat::BC4_UNORM:
+ return "BC4_UNORM";
+ case DXGIFormat::BC4_SNORM:
+ return "BC4_SNORM";
+ case DXGIFormat::BC5_UNORM:
+ return "BC5_UNORM";
+ case DXGIFormat::BC5_SNORM:
+ return "BC5_SNORM";
+ case DXGIFormat::BC6H_UF16:
+ return "BC6H_UF16";
+ case DXGIFormat::BC6H_SF16:
+ return "BC6H_SF16";
+ case DXGIFormat::BC7_UNORM:
+ return "BC7_UNORM";
+ case DXGIFormat::BC7_UNORM_SRGB:
+ return "BC7_UNORM_SRGB";
+ case DXGIFormat::R8G8B8A8_UNORM:
+ return "R8G8B8A8_UNORM";
+ case DXGIFormat::R8G8B8A8_UNORM_SRGB:
+ return "R8G8B8A8_UNORM_SRGB";
+ case DXGIFormat::B8G8R8A8_UNORM:
+ return "B8G8R8A8_UNORM";
+ case DXGIFormat::R16G16B16A16_FLOAT:
+ return "R16G16B16A16_FLOAT";
+ case DXGIFormat::R32G32B32A32_FLOAT:
+ return "R32G32B32A32_FLOAT";
+ default:
+ return "Unknown";
+ }
+}
+
+QString formatDescription(const DDSPixelFormat& pf, const DDSHeaderDXT10* dxt10)
+{
+ if (dxt10) {
+ return QString("DXGI: %1").arg(dxgiFormatName(dxt10->dxgiFormat));
+ }
+
+ if (pf.dwFlags & DDPF_FOURCC) {
+ char cc[5] = {0};
+ std::memcpy(cc, &pf.dwFourCC, 4);
+ DXGIFormat dxgi = fourCCToDXGI(pf.dwFourCC);
+ if (dxgi != DXGIFormat::UNKNOWN) {
+ return QString("FourCC: %1 (%2)").arg(cc).arg(dxgiFormatName(dxgi));
+ }
+ return QString("FourCC: %1").arg(cc);
+ }
+
+ return QString("%1-bit R:0x%2 G:0x%3 B:0x%4 A:0x%5")
+ .arg(pf.dwRGBBitCount)
+ .arg(pf.dwRBitMask, 0, 16)
+ .arg(pf.dwGBitMask, 0, 16)
+ .arg(pf.dwBBitMask, 0, 16)
+ .arg(pf.dwABitMask, 0, 16);
+}