Vita3K/external/ddspp/ddspp.h

1055 lines
36 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);
}
}
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) + 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;
unsigned int width = desc.width;
unsigned int height = desc.height;
for (unsigned int m = 0; m < desc.numMips; ++m)
{
unsigned long long blocksX = (width + desc.blockWidth - 1) / desc.blockWidth;
unsigned long long blocksY = (height + desc.blockHeight - 1) / desc.blockHeight;
unsigned long long mipSize = blocksX * blocksY * desc.bitsPerPixelOrBlock;
mipChainSize += mipSize;
width >>= 1;
height >>= 1;
}
offset += mipChainSize * slice;
width = desc.width;
height = desc.height;
for (unsigned int m = 0; m < mip; ++m)
{
unsigned long long blocksX = (width + desc.blockWidth - 1) / desc.blockWidth;
unsigned long long blocksY = (height + desc.blockHeight - 1) / desc.blockHeight;
unsigned long long mipSize = blocksX * blocksY * desc.bitsPerPixelOrBlock;
offset += mipSize > desc.bitsPerPixelOrBlock ? mipSize : desc.bitsPerPixelOrBlock;
width >>= 1;
height >>= 1;
}
}
offset /= 8; // Back to bytes
return (unsigned int)offset;
}
}