16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 18:09:18 +02:00
Files
2025-04-15 01:32:56 +02:00

1123 lines
35 KiB
C++

#pragma once
// Sources
// https://learn.microsoft.com/en-us/windows/uwp/gaming/complete-code-for-ddstextureloader
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header-dxt10
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-pixelformat
#if (__cpp_constexpr >= 201304) || (_MSC_VER > 1900)
#define ddspp_constexpr constexpr
#else
#define ddspp_constexpr const
#endif
namespace ddspp {
namespace internal {
static ddspp_constexpr unsigned int DDS_MAGIC = 0x20534444;
static ddspp_constexpr unsigned int DDS_ALPHAPIXELS = 0x00000001;
static ddspp_constexpr unsigned int DDS_ALPHA = 0x00000002; // DDPF_ALPHA
static ddspp_constexpr unsigned int DDS_FOURCC = 0x00000004; // DDPF_FOURCC
static ddspp_constexpr unsigned int DDS_RGB = 0x00000040; // DDPF_RGB
static ddspp_constexpr unsigned int DDS_RGBA =
0x00000041; // DDPF_RGB | DDPF_ALPHAPIXELS
static ddspp_constexpr unsigned int DDS_YUV = 0x00000200; // DDPF_YUV
static ddspp_constexpr unsigned int DDS_LUMINANCE =
0x00020000; // DDPF_LUMINANCE
static ddspp_constexpr unsigned int DDS_LUMINANCEA =
0x00020001; // DDPF_LUMINANCE | DDPF_ALPHAPIXELS
static ddspp_constexpr unsigned int DDS_PAL8 =
0x00000020; // DDPF_PALETTEINDEXED8
static ddspp_constexpr unsigned int DDS_BUMPDUDV = 0x00080000; // DDPF_BUMPDUDV
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_CAPS =
0x00000001; // DDSD_CAPS
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_HEIGHT =
0x00000002; // DDSD_HEIGHT
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_WIDTH =
0x00000004; // DDSD_WIDTH
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_PITCH =
0x00000008; // DDSD_PITCH
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_PIXELFORMAT =
0x00001000; // DDSD_PIXELFORMAT
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_MIPMAP =
0x00020000; // DDSD_MIPMAPCOUNT
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_LINEARSIZE =
0x00080000; // DDSD_LINEARSIZE
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_VOLUME =
0x00800000; // DDSD_DEPTH
static ddspp_constexpr unsigned int DDS_HEADER_CAPS_COMPLEX =
0x00000008; // DDSCAPS_COMPLEX
static ddspp_constexpr unsigned int DDS_HEADER_CAPS_MIPMAP =
0x00400000; // DDSCAPS_MIPMAP
static ddspp_constexpr unsigned int DDS_HEADER_CAPS_TEXTURE =
0x00001000; // DDSCAPS_TEXTURE
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP =
0x00000200; // DDSCAPS2_CUBEMAP
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_POSITIVEX =
0x00000600; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEX =
0x00000a00; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_POSITIVEY =
0x00001200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEY =
0x00002200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_POSITIVEZ =
0x00004200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEZ =
0x00008200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_VOLUME =
0x00200000; // DDSCAPS2_VOLUME
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_ALLFACES =
DDS_HEADER_CAPS2_CUBEMAP_POSITIVEX | DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEX |
DDS_HEADER_CAPS2_CUBEMAP_POSITIVEY | DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEY |
DDS_HEADER_CAPS2_CUBEMAP_POSITIVEZ | DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEZ;
static ddspp_constexpr unsigned int DXGI_MISC_FLAG_CUBEMAP =
0x2; // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_resource_misc_flag
static ddspp_constexpr unsigned int DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7;
#define ddspp_make_fourcc(a, b, c, d) \
((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
// FOURCC constants
static ddspp_constexpr unsigned int FOURCC_DXT1 =
ddspp_make_fourcc('D', 'X', 'T', '1'); // BC1_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT2 =
ddspp_make_fourcc('D', 'X', 'T', '2'); // BC2_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT3 =
ddspp_make_fourcc('D', 'X', 'T', '3'); // BC2_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT4 =
ddspp_make_fourcc('D', 'X', 'T', '4'); // BC3_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT5 =
ddspp_make_fourcc('D', 'X', 'T', '5'); // BC3_UNORM
static ddspp_constexpr unsigned int FOURCC_ATI1 =
ddspp_make_fourcc('A', 'T', 'I', '1'); // BC4_UNORM
static ddspp_constexpr unsigned int FOURCC_BC4U =
ddspp_make_fourcc('B', 'C', '4', 'U'); // BC4_UNORM
static ddspp_constexpr unsigned int FOURCC_BC4S =
ddspp_make_fourcc('B', 'C', '4', 'S'); // BC4_SNORM
static ddspp_constexpr unsigned int FOURCC_ATI2 =
ddspp_make_fourcc('A', 'T', 'I', '2'); // BC5_UNORM
static ddspp_constexpr unsigned int FOURCC_BC5U =
ddspp_make_fourcc('B', 'C', '5', 'U'); // BC5_UNORM
static ddspp_constexpr unsigned int FOURCC_BC5S =
ddspp_make_fourcc('B', 'C', '5', 'S'); // BC5_SNORM
static ddspp_constexpr unsigned int FOURCC_RGBG =
ddspp_make_fourcc('R', 'G', 'B', 'G'); // R8G8_B8G8_UNORM
static ddspp_constexpr unsigned int FOURCC_GRBG =
ddspp_make_fourcc('G', 'R', 'G', 'B'); // G8R8_G8B8_UNORM
static ddspp_constexpr unsigned int FOURCC_YUY2 =
ddspp_make_fourcc('Y', 'U', 'Y', '2'); // YUY2
static ddspp_constexpr unsigned int FOURCC_DXT10 =
ddspp_make_fourcc('D', 'X', '1', '0'); // DDS extension header
// These values come from the original D3D9 D3DFORMAT values
// https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dformat
static ddspp_constexpr unsigned int FOURCC_RGB8 = 20;
static ddspp_constexpr unsigned int FOURCC_A8R8G8B8 = 21;
static ddspp_constexpr unsigned int FOURCC_X8R8G8B8 = 22;
static ddspp_constexpr unsigned int FOURCC_R5G6B5 =
23; // B5G6R5_UNORM (needs swizzling)
static ddspp_constexpr unsigned int FOURCC_X1R5G5B5 = 24;
static ddspp_constexpr unsigned int FOURCC_RGB5A1 =
25; // B5G5R5A1_UNORM (needs swizzling)
static ddspp_constexpr unsigned int FOURCC_RGBA4 =
26; // B4G4R4A4_UNORM (needs swizzling)
static ddspp_constexpr unsigned int FOURCC_R3G3B2 = 27;
static ddspp_constexpr unsigned int FOURCC_A8 = 28;
static ddspp_constexpr unsigned int FOURCC_A8R3G3B2 = 29;
static ddspp_constexpr unsigned int FOURCC_X4R4G4B4 = 30;
static ddspp_constexpr unsigned int FOURCC_A2B10G10R10 = 31;
static ddspp_constexpr unsigned int FOURCC_A8B8G8R8 = 32;
static ddspp_constexpr unsigned int FOURCC_X8B8G8R8 = 33;
static ddspp_constexpr unsigned int FOURCC_G16R16 = 34;
static ddspp_constexpr unsigned int FOURCC_A2R10G10B10 = 35;
static ddspp_constexpr unsigned int FOURCC_RGBA16U = 36; // R16G16B16A16_UNORM
static ddspp_constexpr unsigned int FOURCC_RGBA16S = 110; // R16G16B16A16_SNORM
static ddspp_constexpr unsigned int FOURCC_R16F = 111; // R16_FLOAT
static ddspp_constexpr unsigned int FOURCC_RG16F = 112; // R16G16_FLOAT
static ddspp_constexpr unsigned int FOURCC_RGBA16F = 113; // R16G16B16A16_FLOAT
static ddspp_constexpr unsigned int FOURCC_R32F = 114; // R32_FLOAT
static ddspp_constexpr unsigned int FOURCC_RG32F = 115; // R32G32_FLOAT
static ddspp_constexpr unsigned int FOURCC_RGBA32F = 116; // R32G32B32A32_FLOAT
struct PixelFormat {
unsigned int size;
unsigned int flags;
unsigned int fourCC;
unsigned int RGBBitCount;
unsigned int RBitMask;
unsigned int GBitMask;
unsigned int BBitMask;
unsigned int ABitMask;
};
static_assert(sizeof(PixelFormat) == 32, "PixelFormat size mismatch");
inline ddspp_constexpr bool is_rgba_mask(const PixelFormat& ddspf,
unsigned int rmask, unsigned int gmask,
unsigned int bmask,
unsigned int amask) {
return (ddspf.RBitMask == rmask) && (ddspf.GBitMask == gmask) &&
(ddspf.BBitMask == bmask) && (ddspf.ABitMask == amask);
}
inline ddspp_constexpr bool is_rgb_mask(const PixelFormat& ddspf,
unsigned int rmask, unsigned int gmask,
unsigned int bmask) {
return (ddspf.RBitMask == rmask) && (ddspf.GBitMask == gmask) &&
(ddspf.BBitMask == bmask);
}
} // namespace internal
using namespace internal;
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/ne-d3d11-d3d11_resource_dimension
enum DXGIResourceDimension : unsigned char {
DXGI_Unknown,
DXGI_Buffer,
DXGI_Texture1D,
DXGI_Texture2D,
DXGI_Texture3D
};
// Matches DXGI_FORMAT
// https://docs.microsoft.com/en-us/windows/desktop/api/dxgiformat/ne-dxgiformat-dxgi_format
enum DXGIFormat : unsigned int {
UNKNOWN = 0,
R32G32B32A32_TYPELESS = 1,
R32G32B32A32_FLOAT = 2,
R32G32B32A32_UINT = 3,
R32G32B32A32_SINT = 4,
R32G32B32_TYPELESS = 5,
R32G32B32_FLOAT = 6,
R32G32B32_UINT = 7,
R32G32B32_SINT = 8,
R16G16B16A16_TYPELESS = 9,
R16G16B16A16_FLOAT = 10,
R16G16B16A16_UNORM = 11,
R16G16B16A16_UINT = 12,
R16G16B16A16_SNORM = 13,
R16G16B16A16_SINT = 14,
R32G32_TYPELESS = 15,
R32G32_FLOAT = 16,
R32G32_UINT = 17,
R32G32_SINT = 18,
R32G8X24_TYPELESS = 19,
D32_FLOAT_S8X24_UINT = 20,
R32_FLOAT_X8X24_TYPELESS = 21,
X32_TYPELESS_G8X24_UINT = 22,
R10G10B10A2_TYPELESS = 23,
R10G10B10A2_UNORM = 24,
R10G10B10A2_UINT = 25,
R11G11B10_FLOAT = 26,
R8G8B8A8_TYPELESS = 27,
R8G8B8A8_UNORM = 28,
R8G8B8A8_UNORM_SRGB = 29,
R8G8B8A8_UINT = 30,
R8G8B8A8_SNORM = 31,
R8G8B8A8_SINT = 32,
R16G16_TYPELESS = 33,
R16G16_FLOAT = 34,
R16G16_UNORM = 35,
R16G16_UINT = 36,
R16G16_SNORM = 37,
R16G16_SINT = 38,
R32_TYPELESS = 39,
D32_FLOAT = 40,
R32_FLOAT = 41,
R32_UINT = 42,
R32_SINT = 43,
R24G8_TYPELESS = 44,
D24_UNORM_S8_UINT = 45,
R24_UNORM_X8_TYPELESS = 46,
X24_TYPELESS_G8_UINT = 47,
R8G8_TYPELESS = 48,
R8G8_UNORM = 49,
R8G8_UINT = 50,
R8G8_SNORM = 51,
R8G8_SINT = 52,
R16_TYPELESS = 53,
R16_FLOAT = 54,
D16_UNORM = 55,
R16_UNORM = 56,
R16_UINT = 57,
R16_SNORM = 58,
R16_SINT = 59,
R8_TYPELESS = 60,
R8_UNORM = 61,
R8_UINT = 62,
R8_SNORM = 63,
R8_SINT = 64,
A8_UNORM = 65,
R1_UNORM = 66,
R9G9B9E5_SHAREDEXP = 67,
R8G8_B8G8_UNORM = 68,
G8R8_G8B8_UNORM = 69,
BC1_TYPELESS = 70,
BC1_UNORM = 71,
BC1_UNORM_SRGB = 72,
BC2_TYPELESS = 73,
BC2_UNORM = 74,
BC2_UNORM_SRGB = 75,
BC3_TYPELESS = 76,
BC3_UNORM = 77,
BC3_UNORM_SRGB = 78,
BC4_TYPELESS = 79,
BC4_UNORM = 80,
BC4_SNORM = 81,
BC5_TYPELESS = 82,
BC5_UNORM = 83,
BC5_SNORM = 84,
B5G6R5_UNORM = 85,
B5G5R5A1_UNORM = 86,
B8G8R8A8_UNORM = 87,
B8G8R8X8_UNORM = 88,
R10G10B10_XR_BIAS_A2_UNORM = 89,
B8G8R8A8_TYPELESS = 90,
B8G8R8A8_UNORM_SRGB = 91,
B8G8R8X8_TYPELESS = 92,
B8G8R8X8_UNORM_SRGB = 93,
BC6H_TYPELESS = 94,
BC6H_UF16 = 95,
BC6H_SF16 = 96,
BC7_TYPELESS = 97,
BC7_UNORM = 98,
BC7_UNORM_SRGB = 99,
AYUV = 100,
Y410 = 101,
Y416 = 102,
NV12 = 103,
P010 = 104,
P016 = 105,
OPAQUE_420 = 106,
YUY2 = 107,
Y210 = 108,
Y216 = 109,
NV11 = 110,
AI44 = 111,
IA44 = 112,
P8 = 113,
A8P8 = 114,
B4G4R4A4_UNORM = 115,
P208 = 130,
V208 = 131,
V408 = 132,
ASTC_4X4_TYPELESS = 133,
ASTC_4X4_UNORM = 134,
ASTC_4X4_UNORM_SRGB = 135,
ASTC_5X4_TYPELESS = 137,
ASTC_5X4_UNORM = 138,
ASTC_5X4_UNORM_SRGB = 139,
ASTC_5X5_TYPELESS = 141,
ASTC_5X5_UNORM = 142,
ASTC_5X5_UNORM_SRGB = 143,
ASTC_6X5_TYPELESS = 145,
ASTC_6X5_UNORM = 146,
ASTC_6X5_UNORM_SRGB = 147,
ASTC_6X6_TYPELESS = 149,
ASTC_6X6_UNORM = 150,
ASTC_6X6_UNORM_SRGB = 151,
ASTC_8X5_TYPELESS = 153,
ASTC_8X5_UNORM = 154,
ASTC_8X5_UNORM_SRGB = 155,
ASTC_8X6_TYPELESS = 157,
ASTC_8X6_UNORM = 158,
ASTC_8X6_UNORM_SRGB = 159,
ASTC_8X8_TYPELESS = 161,
ASTC_8X8_UNORM = 162,
ASTC_8X8_UNORM_SRGB = 163,
ASTC_10X5_TYPELESS = 165,
ASTC_10X5_UNORM = 166,
ASTC_10X5_UNORM_SRGB = 167,
ASTC_10X6_TYPELESS = 169,
ASTC_10X6_UNORM = 170,
ASTC_10X6_UNORM_SRGB = 171,
ASTC_10X8_TYPELESS = 173,
ASTC_10X8_UNORM = 174,
ASTC_10X8_UNORM_SRGB = 175,
ASTC_10X10_TYPELESS = 177,
ASTC_10X10_UNORM = 178,
ASTC_10X10_UNORM_SRGB = 179,
ASTC_12X10_TYPELESS = 181,
ASTC_12X10_UNORM = 182,
ASTC_12X10_UNORM_SRGB = 183,
ASTC_12X12_TYPELESS = 185,
ASTC_12X12_UNORM = 186,
ASTC_12X12_UNORM_SRGB = 187,
FORCE_UINT = 0xffffffff
};
struct Header {
unsigned int size;
unsigned int flags;
unsigned int height;
unsigned int width;
unsigned int pitchOrLinearSize;
unsigned int depth;
unsigned int mipMapCount;
unsigned int reserved1[11];
PixelFormat ddspf;
unsigned int caps;
unsigned int caps2;
unsigned int caps3;
unsigned int caps4;
unsigned int reserved2;
};
static_assert(sizeof(Header) == 124, "DDS Header size mismatch");
struct HeaderDXT10 {
DXGIFormat dxgiFormat;
DXGIResourceDimension resourceDimension;
unsigned int miscFlag;
unsigned int arraySize;
unsigned int reserved;
};
static_assert(sizeof(HeaderDXT10) == 20,
"DDS DX10 Extended Header size mismatch");
// Maximum possible size of header. Use this to read in only the header, decode,
// seek to the real header size, then read in the rest of the image data
ddspp_constexpr int MAX_HEADER_SIZE =
sizeof(DDS_MAGIC) + sizeof(Header) + sizeof(HeaderDXT10);
enum Result : unsigned char { Success, Error };
enum TextureType : unsigned char {
Texture1D,
Texture2D,
Texture3D,
Cubemap,
};
struct Descriptor {
DXGIFormat format;
TextureType type;
unsigned int width;
unsigned int height;
unsigned int depth;
unsigned int numMips;
unsigned int arraySize;
unsigned int rowPitch; // Row pitch for mip 0
unsigned int depthPitch; // Size of mip 0
unsigned int
bitsPerPixelOrBlock; // If compressed bits per block, else bits per pixel
unsigned int blockWidth; // Width of block in pixels (1 if uncompressed)
unsigned int blockHeight; // Height of block in pixels (1 if uncompressed)
bool compressed;
bool srgb;
unsigned int
headerSize; // Actual size of header, use this to get to image data
};
inline ddspp_constexpr bool is_dxt10(const Header& header) {
const PixelFormat& ddspf = header.ddspf;
return (ddspf.flags & DDS_FOURCC) && (ddspf.fourCC == FOURCC_DXT10);
}
inline ddspp_constexpr bool is_compressed(DXGIFormat format) {
return (format >= BC1_UNORM && format <= BC5_SNORM) ||
(format >= BC6H_TYPELESS && format <= BC7_UNORM_SRGB) ||
(format >= ASTC_4X4_TYPELESS && format <= ASTC_12X12_UNORM_SRGB);
}
inline ddspp_constexpr bool is_srgb(DXGIFormat format) {
switch (format) {
case R8G8B8A8_UNORM_SRGB:
case BC1_UNORM_SRGB:
case BC2_UNORM_SRGB:
case BC3_UNORM_SRGB:
case B8G8R8A8_UNORM_SRGB:
case B8G8R8X8_UNORM_SRGB:
case BC7_UNORM_SRGB:
case ASTC_4X4_UNORM_SRGB:
case ASTC_5X4_UNORM_SRGB:
case ASTC_5X5_UNORM_SRGB:
case ASTC_6X5_UNORM_SRGB:
case ASTC_6X6_UNORM_SRGB:
case ASTC_8X5_UNORM_SRGB:
case ASTC_8X6_UNORM_SRGB:
case ASTC_8X8_UNORM_SRGB:
case ASTC_10X5_UNORM_SRGB:
case ASTC_10X6_UNORM_SRGB:
case ASTC_10X8_UNORM_SRGB:
case ASTC_10X10_UNORM_SRGB:
case ASTC_12X10_UNORM_SRGB:
case ASTC_12X12_UNORM_SRGB:
return true;
default:
return false;
}
}
inline ddspp_constexpr unsigned int get_bits_per_pixel_or_block(
DXGIFormat format) {
if (format >= ASTC_4X4_TYPELESS && format <= ASTC_12X12_UNORM_SRGB) {
return 128; // All ASTC blocks are the same size
}
switch (format) {
case R1_UNORM:
return 1;
case R8_TYPELESS:
case R8_UNORM:
case R8_UINT:
case R8_SNORM:
case R8_SINT:
case A8_UNORM:
case AI44:
case IA44:
case P8:
return 8;
case NV12:
case OPAQUE_420:
case NV11:
return 12;
case R8G8_TYPELESS:
case R8G8_UNORM:
case R8G8_UINT:
case R8G8_SNORM:
case R8G8_SINT:
case R16_TYPELESS:
case R16_FLOAT:
case D16_UNORM:
case R16_UNORM:
case R16_UINT:
case R16_SNORM:
case R16_SINT:
case B5G6R5_UNORM:
case B5G5R5A1_UNORM:
case A8P8:
case B4G4R4A4_UNORM:
return 16;
case P010:
case P016:
return 24;
case BC1_UNORM:
case BC1_UNORM_SRGB:
case BC1_TYPELESS:
case BC4_UNORM:
case BC4_SNORM:
case BC4_TYPELESS:
case R16G16B16A16_TYPELESS:
case R16G16B16A16_FLOAT:
case R16G16B16A16_UNORM:
case R16G16B16A16_UINT:
case R16G16B16A16_SNORM:
case R16G16B16A16_SINT:
case R32G32_TYPELESS:
case R32G32_FLOAT:
case R32G32_UINT:
case R32G32_SINT:
case R32G8X24_TYPELESS:
case D32_FLOAT_S8X24_UINT:
case R32_FLOAT_X8X24_TYPELESS:
case X32_TYPELESS_G8X24_UINT:
case Y416:
case Y210:
case Y216:
return 64;
case R32G32B32_TYPELESS:
case R32G32B32_FLOAT:
case R32G32B32_UINT:
case R32G32B32_SINT:
return 96;
case BC2_UNORM:
case BC2_UNORM_SRGB:
case BC2_TYPELESS:
case BC3_UNORM:
case BC3_UNORM_SRGB:
case BC3_TYPELESS:
case BC5_UNORM:
case BC5_SNORM:
case BC6H_UF16:
case BC6H_SF16:
case BC7_UNORM:
case BC7_UNORM_SRGB:
case R32G32B32A32_TYPELESS:
case R32G32B32A32_FLOAT:
case R32G32B32A32_UINT:
case R32G32B32A32_SINT:
return 128;
default:
return 32; // Most formats are 32 bits per pixel
break;
}
}
inline ddspp_constexpr void get_block_size(DXGIFormat format,
unsigned int& blockWidth,
unsigned int& blockHeight) {
switch (format) {
case BC1_UNORM:
case BC1_UNORM_SRGB:
case BC1_TYPELESS:
case BC4_UNORM:
case BC4_SNORM:
case BC4_TYPELESS:
case BC2_UNORM:
case BC2_UNORM_SRGB:
case BC2_TYPELESS:
case BC3_UNORM:
case BC3_UNORM_SRGB:
case BC3_TYPELESS:
case BC5_UNORM:
case BC5_SNORM:
case BC6H_UF16:
case BC6H_SF16:
case BC7_UNORM:
case BC7_UNORM_SRGB:
case ASTC_4X4_TYPELESS:
case ASTC_4X4_UNORM:
case ASTC_4X4_UNORM_SRGB:
blockWidth = 4;
blockHeight = 4;
break;
case ASTC_5X4_TYPELESS:
case ASTC_5X4_UNORM:
case ASTC_5X4_UNORM_SRGB:
blockWidth = 5;
blockHeight = 4;
break;
case ASTC_5X5_TYPELESS:
case ASTC_5X5_UNORM:
case ASTC_5X5_UNORM_SRGB:
blockWidth = 5;
blockHeight = 5;
break;
case ASTC_6X5_TYPELESS:
case ASTC_6X5_UNORM:
case ASTC_6X5_UNORM_SRGB:
blockWidth = 6;
blockHeight = 5;
case ASTC_6X6_TYPELESS:
case ASTC_6X6_UNORM:
case ASTC_6X6_UNORM_SRGB:
blockWidth = 6;
blockHeight = 6;
case ASTC_8X5_TYPELESS:
case ASTC_8X5_UNORM:
case ASTC_8X5_UNORM_SRGB:
blockWidth = 8;
blockHeight = 5;
case ASTC_8X6_TYPELESS:
case ASTC_8X6_UNORM:
case ASTC_8X6_UNORM_SRGB:
blockWidth = 8;
blockHeight = 6;
case ASTC_8X8_TYPELESS:
case ASTC_8X8_UNORM:
case ASTC_8X8_UNORM_SRGB:
blockWidth = 8;
blockHeight = 8;
case ASTC_10X5_TYPELESS:
case ASTC_10X5_UNORM:
case ASTC_10X5_UNORM_SRGB:
blockWidth = 10;
blockHeight = 5;
case ASTC_10X6_TYPELESS:
case ASTC_10X6_UNORM:
case ASTC_10X6_UNORM_SRGB:
blockWidth = 10;
blockHeight = 6;
case ASTC_10X8_TYPELESS:
case ASTC_10X8_UNORM:
case ASTC_10X8_UNORM_SRGB:
blockWidth = 10;
blockHeight = 8;
case ASTC_10X10_TYPELESS:
case ASTC_10X10_UNORM:
case ASTC_10X10_UNORM_SRGB:
blockWidth = 10;
blockHeight = 10;
case ASTC_12X10_TYPELESS:
case ASTC_12X10_UNORM:
case ASTC_12X10_UNORM_SRGB:
blockWidth = 12;
blockHeight = 10;
case ASTC_12X12_TYPELESS:
case ASTC_12X12_UNORM:
case ASTC_12X12_UNORM_SRGB:
blockWidth = 12;
blockHeight = 12;
default:
blockWidth = 1;
blockHeight = 1;
break;
}
return;
}
inline ddspp_constexpr unsigned int get_row_pitch(
unsigned int width, unsigned int bitsPerPixelOrBlock,
unsigned int blockWidth, unsigned int mip) {
// Shift width by mipmap index, round to next block size and round to next
// byte (for the rare less than 1 byte per pixel formats) E.g. width = 119,
// mip = 3, BC1 compression
// ((((119 >> 2) + 4 - 1) / 4) * 64) / 8 = 64 bytes
return ((((((width >> mip) > 1) ? (width >> mip) : 1) + blockWidth - 1) /
blockWidth) *
bitsPerPixelOrBlock +
7) /
8;
}
// Returns number of bytes for each row of a given mip. Valid range is [0,
// desc.numMips)
inline ddspp_constexpr unsigned int get_row_pitch(const Descriptor& desc,
unsigned int mip) {
return get_row_pitch(desc.width, desc.bitsPerPixelOrBlock, desc.blockWidth,
mip);
}
inline Result decode_header(unsigned char* sourceData, Descriptor& desc) {
unsigned int magic = *reinterpret_cast<unsigned int*>(
sourceData); // First 4 bytes are the magic DDS number
if (magic != DDS_MAGIC) {
return ddspp::Error;
}
const Header header =
*reinterpret_cast<const Header*>(sourceData + sizeof(DDS_MAGIC));
const PixelFormat& ddspf = header.ddspf;
bool dxt10Extension = is_dxt10(header);
const HeaderDXT10 dxt10Header = *reinterpret_cast<const HeaderDXT10*>(
sourceData + sizeof(DDS_MAGIC) + sizeof(Header));
// Read basic data from the header
desc.width = header.width > 0 ? header.width : 1;
desc.height = header.height > 0 ? header.height : 1;
desc.depth = header.depth > 0 ? header.depth : 1;
desc.numMips = header.mipMapCount > 0 ? header.mipMapCount : 1;
// Set some sensible defaults
desc.arraySize = 1;
desc.srgb = false;
desc.type = Texture2D;
desc.format = UNKNOWN;
if (dxt10Extension) {
desc.format = dxt10Header.dxgiFormat;
desc.arraySize = dxt10Header.arraySize;
switch (dxt10Header.resourceDimension) {
case DXGI_Texture1D:
desc.depth = 1;
desc.type = Texture1D;
break;
case DXGI_Texture2D:
desc.depth = 1;
if (dxt10Header.miscFlag & DXGI_MISC_FLAG_CUBEMAP) {
desc.type = Cubemap;
} else {
desc.type = Texture2D;
}
break;
case DXGI_Texture3D:
desc.type = Texture3D;
desc.arraySize = 1; // There are no 3D texture arrays
break;
default:
break;
}
} else {
if (ddspf.flags & DDS_FOURCC) {
unsigned int fourCC = ddspf.fourCC;
switch (fourCC) {
// Compressed
case FOURCC_DXT1:
desc.format = BC1_UNORM;
break;
case FOURCC_DXT2: // fallthrough
case FOURCC_DXT3:
desc.format = BC2_UNORM;
break;
case FOURCC_DXT4: // fallthrough
case FOURCC_DXT5:
desc.format = BC3_UNORM;
break;
case FOURCC_ATI1: // fallthrough
case FOURCC_BC4U:
desc.format = BC4_UNORM;
break;
case FOURCC_BC4S:
desc.format = BC4_SNORM;
break;
case FOURCC_ATI2: // fallthrough
case FOURCC_BC5U:
desc.format = BC5_UNORM;
break;
case FOURCC_BC5S:
desc.format = BC5_SNORM;
break;
// Video
case FOURCC_RGBG:
desc.format = R8G8_B8G8_UNORM;
break;
case FOURCC_GRBG:
desc.format = G8R8_G8B8_UNORM;
break;
case FOURCC_YUY2:
desc.format = YUY2;
break;
// Packed
case FOURCC_R5G6B5:
desc.format = B5G6R5_UNORM;
break;
case FOURCC_RGB5A1:
desc.format = B5G5R5A1_UNORM;
break;
case FOURCC_RGBA4:
desc.format = B4G4R4A4_UNORM;
break;
// Uncompressed
case FOURCC_A8:
desc.format = R8_UNORM;
break;
case FOURCC_A2B10G10R10:
desc.format = R10G10B10A2_UNORM;
break;
case FOURCC_RGBA16U:
desc.format = R16G16B16A16_UNORM;
break;
case FOURCC_RGBA16S:
desc.format = R16G16B16A16_SNORM;
break;
case FOURCC_R16F:
desc.format = R16_FLOAT;
break;
case FOURCC_RG16F:
desc.format = R16G16_FLOAT;
break;
case FOURCC_RGBA16F:
desc.format = R16G16B16A16_FLOAT;
break;
case FOURCC_R32F:
desc.format = R32_FLOAT;
break;
case FOURCC_RG32F:
desc.format = R32G32_FLOAT;
break;
case FOURCC_RGBA32F:
desc.format = R32G32B32A32_FLOAT;
break;
default:
break;
}
} else if (ddspf.flags & DDS_RGB) {
switch (ddspf.RGBBitCount) {
case 32:
if (is_rgba_mask(ddspf, 0x000000ff, 0x0000ff00, 0x00ff0000,
0xff000000)) {
desc.format = R8G8B8A8_UNORM;
} else if (is_rgba_mask(ddspf, 0x00ff0000, 0x0000ff00, 0x000000ff,
0xff000000)) {
desc.format = B8G8R8A8_UNORM;
} else if (is_rgba_mask(ddspf, 0x00ff0000, 0x0000ff00, 0x000000ff,
0x00000000)) {
desc.format = B8G8R8X8_UNORM;
}
// No DXGI format maps to (0x000000ff, 0x0000ff00, 0x00ff0000,
// 0x00000000) aka D3DFMT_X8B8G8R8 No DXGI format maps to (0x000003ff,
// 0x000ffc00, 0x3ff00000, 0xc0000000) aka D3DFMT_A2R10G10B10
// Note that many common DDS reader/writers (including D3DX) swap the
// the RED/BLUE masks for 10:10:10:2 formats. We assume
// below that the 'backwards' header mask is being used since it is
// most likely written by D3DX. The more robust solution is to use the
// 'DX10' header extension and specify the
// DXGI_FORMAT_R10G10B10A2_UNORM format directly
// For 'correct' writers, this should be 0x000003ff, 0x000ffc00,
// 0x3ff00000 for RGB data
else if (is_rgba_mask(ddspf, 0x3ff00000, 0x000ffc00, 0x000003ff,
0xc0000000)) {
desc.format = R10G10B10A2_UNORM;
} else if (is_rgba_mask(ddspf, 0x0000ffff, 0xffff0000, 0x00000000,
0x00000000)) {
desc.format = R16G16_UNORM;
} else if (is_rgba_mask(ddspf, 0xffffffff, 0x00000000, 0x00000000,
0x00000000)) {
// The only 32-bit color channel format in D3D9 was R32F
desc.format = R32_FLOAT; // D3DX writes this out as a FourCC of 114
}
break;
case 24:
if (is_rgb_mask(ddspf, 0x00ff0000, 0x0000ff00, 0x000000ff)) {
desc.format = B8G8R8X8_UNORM;
}
break;
case 16:
if (is_rgba_mask(ddspf, 0x7c00, 0x03e0, 0x001f, 0x8000)) {
desc.format = B5G5R5A1_UNORM;
} else if (is_rgba_mask(ddspf, 0xf800, 0x07e0, 0x001f, 0x0000)) {
desc.format = B5G6R5_UNORM;
} else if (is_rgba_mask(ddspf, 0x0f00, 0x00f0, 0x000f, 0xf000)) {
desc.format = B4G4R4A4_UNORM;
}
// No DXGI format maps to (0x7c00, 0x03e0, 0x001f, 0x0000) aka
// D3DFMT_X1R5G5B5. No DXGI format maps to (0x0f00, 0x00f0, 0x000f,
// 0x0000) aka D3DFMT_X4R4G4B4. No 3:3:2, 3:3:2:8, or paletted DXGI
// formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8,
// etc.
break;
default:
break;
}
} else if (ddspf.flags & DDS_LUMINANCE) {
switch (ddspf.RGBBitCount) {
case 16:
if (is_rgba_mask(ddspf, 0x0000ffff, 0x00000000, 0x00000000,
0x00000000)) {
desc.format =
R16_UNORM; // D3DX10/11 writes this out as DX10 extension.
}
if (is_rgba_mask(ddspf, 0x000000ff, 0x00000000, 0x00000000,
0x0000ff00)) {
desc.format =
R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension.
}
break;
case 8:
if (is_rgba_mask(ddspf, 0x000000ff, 0x00000000, 0x00000000,
0x00000000)) {
desc.format =
R8_UNORM; // D3DX10/11 writes this out as DX10 extension
}
// No DXGI format maps to (0x0f, 0x00, 0x00, 0xf0) aka D3DFMT_A4L4
if (is_rgba_mask(ddspf, 0x000000ff, 0x00000000, 0x00000000,
0x0000ff00)) {
desc.format = R8G8_UNORM; // Some DDS writers assume the bitcount
// should be 8 instead of 16
}
break;
}
} else if (ddspf.flags & DDS_ALPHA) {
if (ddspf.RGBBitCount == 8) {
desc.format = A8_UNORM;
}
} else if (ddspf.flags & DDS_BUMPDUDV) {
if (ddspf.RGBBitCount == 32) {
if (is_rgba_mask(ddspf, 0x000000ff, 0x0000ff00, 0x00ff0000,
0xff000000)) {
desc.format =
R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension
}
if (is_rgba_mask(ddspf, 0x0000ffff, 0xffff0000, 0x00000000,
0x00000000)) {
desc.format =
R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension
}
// No DXGI format maps to (0x3ff00000, 0x000ffc00, 0x000003ff,
// 0xc0000000) aka D3DFMT_A2W10V10U10
} else if (ddspf.RGBBitCount == 16) {
if (is_rgba_mask(ddspf, 0x000000ff, 0x0000ff00, 0x00000000,
0x00000000)) {
desc.format =
R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension
}
}
}
if ((header.flags & DDS_HEADER_FLAGS_VOLUME) ||
(header.caps2 & DDS_HEADER_CAPS2_VOLUME)) {
desc.type = Texture3D;
} else if (header.caps2 & DDS_HEADER_CAPS2_CUBEMAP) {
if ((header.caps2 & DDS_HEADER_CAPS2_CUBEMAP_ALLFACES) !=
DDS_HEADER_CAPS2_CUBEMAP_ALLFACES) {
return ddspp::Error;
}
desc.type = Cubemap;
desc.arraySize = 1;
desc.depth = 1;
} else {
desc.type = Texture2D;
}
}
desc.compressed = is_compressed(desc.format);
desc.srgb = is_srgb(desc.format);
desc.bitsPerPixelOrBlock = get_bits_per_pixel_or_block(desc.format);
get_block_size(desc.format, desc.blockWidth, desc.blockHeight);
desc.rowPitch =
get_row_pitch(desc.width, desc.bitsPerPixelOrBlock, desc.blockWidth, 0);
desc.depthPitch = desc.rowPitch * desc.height / desc.blockHeight;
desc.headerSize = sizeof(DDS_MAGIC) + sizeof(Header) +
(dxt10Extension ? sizeof(HeaderDXT10) : 0);
return ddspp::Success;
}
inline void encode_header(const DXGIFormat format, const unsigned int width,
const unsigned int height, const unsigned int depth,
const TextureType type, const unsigned int mipCount,
const unsigned int arraySize, Header& header,
HeaderDXT10& dxt10Header) {
header.size = sizeof(Header);
// Fill in header flags
header.flags = DDS_HEADER_FLAGS_CAPS | DDS_HEADER_FLAGS_HEIGHT |
DDS_HEADER_FLAGS_WIDTH | DDS_HEADER_FLAGS_PIXELFORMAT;
header.caps = DDS_HEADER_CAPS_TEXTURE;
header.caps2 = 0;
if (mipCount > 1) {
header.flags |= DDS_HEADER_FLAGS_MIPMAP;
header.caps |= DDS_HEADER_CAPS_COMPLEX | DDS_HEADER_CAPS_MIPMAP;
}
unsigned int bitsPerPixelOrBlock = get_bits_per_pixel_or_block(format);
if (is_compressed(format)) {
header.flags |= DDS_HEADER_FLAGS_LINEARSIZE;
unsigned int blockWidth, blockHeight;
get_block_size(format, blockWidth, blockHeight);
header.pitchOrLinearSize =
width * height * bitsPerPixelOrBlock / (8 * blockWidth * blockHeight);
} else {
header.flags |= DDS_HEADER_FLAGS_PITCH;
header.pitchOrLinearSize = width * bitsPerPixelOrBlock / 8;
}
header.height = height;
header.width = width;
header.depth = depth;
header.mipMapCount = mipCount;
header.reserved1[0] = header.reserved1[1] = header.reserved1[2] = 0;
header.reserved1[3] = header.reserved1[4] = header.reserved1[5] = 0;
header.reserved1[6] = header.reserved1[7] = header.reserved1[8] = 0;
header.reserved1[9] = header.reserved1[10] = 0;
// Fill in pixel format
PixelFormat& ddspf = header.ddspf;
ddspf.size = sizeof(PixelFormat);
ddspf.fourCC = FOURCC_DXT10;
ddspf.flags = 0;
ddspf.flags |= DDS_FOURCC;
dxt10Header.dxgiFormat = format;
dxt10Header.arraySize = arraySize;
dxt10Header.miscFlag = 0;
if (type == Texture1D) {
dxt10Header.resourceDimension = DXGI_Texture1D;
} else if (type == Texture2D) {
dxt10Header.resourceDimension = DXGI_Texture2D;
} else if (type == Cubemap) {
dxt10Header.resourceDimension = DXGI_Texture2D;
dxt10Header.miscFlag |= DXGI_MISC_FLAG_CUBEMAP;
header.caps |= DDS_HEADER_CAPS_COMPLEX;
header.caps2 |=
DDS_HEADER_CAPS2_CUBEMAP | DDS_HEADER_CAPS2_CUBEMAP_ALLFACES;
} else if (type == Texture3D) {
dxt10Header.resourceDimension = DXGI_Texture3D;
header.flags |= DDS_HEADER_FLAGS_VOLUME;
header.caps2 |= DDS_HEADER_CAPS2_VOLUME;
}
// dxt10Header.miscFlag TODO Alpha Mode
// Unused
header.caps3 = 0;
header.caps4 = 0;
header.reserved2 = 0;
}
// Returns the offset from the base pointer to the desired mip and slice
// Slice is either a texture from an array, a face from a cubemap, or a 2D slice
// of a volume texture
inline ddspp_constexpr unsigned int get_offset(const Descriptor& desc,
const unsigned int mip,
const unsigned int slice) {
// The mip/slice arrangement is different between texture arrays and volume
// textures
//
// Arrays
// __________ _____ __ __________ _____ __ __________ _____ __
// | || ||__|| || ||__|| || ||__|
// | ||_____| | ||_____| | ||_____|
// | | | | | |
// |__________| |__________| |__________|
//
// Volume
// __________ __________ __________ _____ _____ _____ __ __ __
// | || || || || || ||__||__||__|
// | || || ||_____||_____||_____|
// | || || |
// |__________||__________||__________|
//
unsigned long long offset = 0;
unsigned long long mip0Size = desc.depthPitch * 8; // Work in bits
if (desc.type == Texture3D) {
for (unsigned int m = 0; m < mip; ++m) {
unsigned long long mipSize = mip0Size >> 2 * m;
offset += mipSize * desc.numMips;
}
unsigned long long lastMip = mip0Size >> 2 * mip;
offset += lastMip * slice;
} else {
unsigned long long mipChainSize = 0;
for (unsigned int m = 0; m < desc.numMips; ++m) {
unsigned long long mipSize =
mip0Size >> 2 * m; // Divide by 2 in width and height
mipChainSize += mipSize > desc.bitsPerPixelOrBlock
? mipSize
: desc.bitsPerPixelOrBlock;
}
offset += mipChainSize * slice;
for (unsigned int m = 0; m < mip; ++m) {
unsigned long long mipSize =
mip0Size >> 2 * m; // Divide by 2 in width and height
offset += mipSize > desc.bitsPerPixelOrBlock ? mipSize
: desc.bitsPerPixelOrBlock;
}
}
offset /= 8; // Back to bytes
return (unsigned int)offset;
}
} // namespace ddspp