Add support for loading DDS files that contain raw/uncompressed pixel data. Resolves issue #1097.

This commit is contained in:
Alex Szpakowski
2019-09-17 22:40:11 -03:00
parent a6f8e7dac6
commit d6e37ec4aa
5 changed files with 578 additions and 200 deletions
+3 -2
View File
@@ -1,7 +1,7 @@
/** /**
* Simple DDS data parser for compressed 2D textures. * Simple DDS data parser for compressed 2D textures.
* *
* Copyright (c) 2013-2017 Alex Szpakowski * Copyright (c) 2013-2019 Alex Szpakowski
* *
* This software is provided 'as-is', without any express or implied * This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages * warranty. In no event will the authors be held liable for any damages
@@ -41,7 +41,8 @@ enum DDPF
DDPF_FOURCC = 0x000004, DDPF_FOURCC = 0x000004,
DDPF_RGB = 0x000040, DDPF_RGB = 0x000040,
DDPF_YUV = 0x000200, DDPF_YUV = 0x000200,
DDPF_LUMINANCE = 0x020000 DDPF_LUMINANCE = 0x020000,
DDPF_BUMPDUDV = 0x080000,
}; };
enum D3D10ResourceDimension enum D3D10ResourceDimension
+389 -113
View File
@@ -1,7 +1,7 @@
/** /**
* Simple DDS data parser for compressed 2D textures. * Simple DDS data parser for compressed 2D textures.
* *
* Copyright (c) 2013-2017 Alex Szpakowski * Copyright (c) 2013-2019 Alex Szpakowski
* *
* This software is provided 'as-is', without any express or implied * This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages * warranty. In no event will the authors be held liable for any damages
@@ -33,107 +33,347 @@ using namespace dds::dxinfo;
// Creates a packed uint representation of a FourCC code. // Creates a packed uint representation of a FourCC code.
#define MakeFourCC(a, b, c, d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a))) #define MakeFourCC(a, b, c, d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)))
// Translate the old DDS format to our own. #define ISBITMASK(r,g,b,a) (ddpf.rBitMask == r && ddpf.gBitMask == g && ddpf.bBitMask == b && ddpf.aBitMask == a)
static Format parseDDSFormat(const DDSPixelFormat &fmt)
// Function adapted from DirectXTex:
// https://github.com/microsoft/DirectXTex/blob/master/DDSTextureLoader/DDSTextureLoader.cpp#L623
static DXGIFormat getDXGIFormat(const DDSPixelFormat& ddpf)
{ {
if ((fmt.flags & DDPF_FOURCC) == 0) if (ddpf.flags & DDPF_RGB)
return FORMAT_UNKNOWN;
Format f = FORMAT_UNKNOWN;
switch (fmt.fourCC)
{ {
case MakeFourCC('D','X','T','1'): // Note that sRGB formats are written using the "DX10" extended header
f = FORMAT_DXT1;
break; switch (ddpf.rgbBitCount)
case MakeFourCC('D','X','T','3'): {
f = FORMAT_DXT3; case 32:
break; if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
case MakeFourCC('D','X','T','5'): return DXGI_FORMAT_R8G8B8A8_UNORM;
f = FORMAT_DXT5;
break; if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000))
case MakeFourCC('A','T','I','1'): return DXGI_FORMAT_B8G8R8A8_UNORM;
case MakeFourCC('B','C','4','U'):
f = FORMAT_BC4; if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000))
break; return DXGI_FORMAT_B8G8R8X8_UNORM;
case MakeFourCC('B','C','4','S'):
f = FORMAT_BC4s; // No DXGI format maps to ISBITMASK(0x000000ff,0x0000ff00,0x00ff0000,0x00000000) aka D3DFMT_X8B8G8R8
break;
case MakeFourCC('A','T','I','2'): // Note that many common DDS reader/writers (including D3DX) swap the
case MakeFourCC('B','C','5','U'): // the RED/BLUE masks for 10:10:10:2 formats. We assume
f = FORMAT_BC5; // below that the 'backwards' header mask is being used since it is most
break; // likely written by D3DX. The more robust solution is to use the 'DX10'
case MakeFourCC('B','C','5','S'): // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly
f = FORMAT_BC5s;
break; // For 'correct' writers, this should be 0x000003ff,0x000ffc00,0x3ff00000 for RGB data
default: if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000))
break; return DXGI_FORMAT_R10G10B10A2_UNORM;
// No DXGI format maps to ISBITMASK(0x000003ff,0x000ffc00,0x3ff00000,0xc0000000) aka D3DFMT_A2R10G10B10
if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000))
return DXGI_FORMAT_R16G16_UNORM;
if (ISBITMASK(0xffffffff, 0x00000000, 0x00000000, 0x00000000))
// Only 32-bit color channel format in D3D9 was R32F
return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114
break;
case 24:
// No 24bpp DXGI formats aka D3DFMT_R8G8B8
break;
case 16:
if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000))
return DXGI_FORMAT_B5G5R5A1_UNORM;
if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0x0000))
return DXGI_FORMAT_B5G6R5_UNORM;
// No DXGI format maps to ISBITMASK(0x7c00,0x03e0,0x001f,0x0000) aka D3DFMT_X1R5G5B5
// No DXGI format maps to ISBITMASK(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;
}
}
else if (ddpf.flags & DDPF_LUMINANCE)
{
if (ddpf.rgbBitCount == 8)
{
if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x00000000))
return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension
// No DXGI format maps to ISBITMASK(0x0f,0x00,0x00,0xf0) aka D3DFMT_A4L4
if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
return DXGI_FORMAT_R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16
}
if (ddpf.rgbBitCount == 16)
{
if (ISBITMASK(0x0000ffff, 0x00000000, 0x00000000, 0x00000000))
return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension
if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension
}
}
else if (ddpf.flags & DDPF_ALPHA)
{
if (ddpf.rgbBitCount == 8)
return DXGI_FORMAT_A8_UNORM;
}
else if (ddpf.flags & DDPF_BUMPDUDV)
{
if (ddpf.rgbBitCount == 16)
{
if (ISBITMASK(0x00ff, 0xff00, 0x0000, 0x0000))
return DXGI_FORMAT_R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension
}
if (ddpf.rgbBitCount == 32)
{
if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
return DXGI_FORMAT_R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension
if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000))
return DXGI_FORMAT_R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension
// No DXGI format maps to ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10
}
}
else if (ddpf.flags & DDPF_FOURCC)
{
switch (ddpf.fourCC)
{
case MakeFourCC('D','X','T','1'):
return DXGI_FORMAT_BC1_UNORM;
case MakeFourCC('D','X','T','3'):
return DXGI_FORMAT_BC2_UNORM;
case MakeFourCC('D','X','T','5'):
return DXGI_FORMAT_BC3_UNORM;
// While pre-multiplied alpha isn't directly supported by the DXGI formats,
// they are basically the same as these BC formats so they can be mapped
case MakeFourCC('D','X','T','2'):
return DXGI_FORMAT_BC2_UNORM;
case MakeFourCC('D','X','T','4'):
return DXGI_FORMAT_BC3_UNORM;
case MakeFourCC('A','T','I','1'):
return DXGI_FORMAT_BC4_UNORM;
case MakeFourCC('B','C','4','U'):
return DXGI_FORMAT_BC4_UNORM;
case MakeFourCC('B','C','4','S'):
return DXGI_FORMAT_BC4_SNORM;
case MakeFourCC('A','T','I','2'):
return DXGI_FORMAT_BC5_UNORM;
case MakeFourCC('B','C','5','U'):
return DXGI_FORMAT_BC5_UNORM;
case MakeFourCC('B','C','5','S'):
return DXGI_FORMAT_BC5_SNORM;
// BC6H and BC7 are written using the "DX10" extended header
case MakeFourCC('R','G','B','G'):
return DXGI_FORMAT_R8G8_B8G8_UNORM;
case MakeFourCC('G','R','G','B'):
return DXGI_FORMAT_G8R8_G8B8_UNORM;
// Check for D3DFORMAT enums being set here
case 36: // D3DFMT_A16B16G16R16
return DXGI_FORMAT_R16G16B16A16_UNORM;
case 110: // D3DFMT_Q16W16V16U16
return DXGI_FORMAT_R16G16B16A16_SNORM;
case 111: // D3DFMT_R16F
return DXGI_FORMAT_R16_FLOAT;
case 112: // D3DFMT_G16R16F
return DXGI_FORMAT_R16G16_FLOAT;
case 113: // D3DFMT_A16B16G16R16F
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case 114: // D3DFMT_R32F
return DXGI_FORMAT_R32_FLOAT;
case 115: // D3DFMT_G32R32F
return DXGI_FORMAT_R32G32_FLOAT;
case 116: // D3DFMT_A32B32G32R32F
return DXGI_FORMAT_R32G32B32A32_FLOAT;
}
} }
return f; return DXGI_FORMAT_UNKNOWN;
} }
// Translate the new DX10 formats to our own. static size_t getBitsPerPixel(DXGIFormat fmt)
static Format parseDX10Format(DXGIFormat fmt)
{ {
Format f = FORMAT_UNKNOWN; switch (fmt)
{
case DXGI_FORMAT_R32G32B32A32_TYPELESS:
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_R32G32B32A32_UINT:
case DXGI_FORMAT_R32G32B32A32_SINT:
return 128;
case DXGI_FORMAT_R32G32B32_TYPELESS:
case DXGI_FORMAT_R32G32B32_FLOAT:
case DXGI_FORMAT_R32G32B32_UINT:
case DXGI_FORMAT_R32G32B32_SINT:
return 96;
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
case DXGI_FORMAT_R16G16B16A16_UNORM:
case DXGI_FORMAT_R16G16B16A16_UINT:
case DXGI_FORMAT_R16G16B16A16_SNORM:
case DXGI_FORMAT_R16G16B16A16_SINT:
case DXGI_FORMAT_R32G32_TYPELESS:
case DXGI_FORMAT_R32G32_FLOAT:
case DXGI_FORMAT_R32G32_UINT:
case DXGI_FORMAT_R32G32_SINT:
case DXGI_FORMAT_R32G8X24_TYPELESS:
case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
return 64;
case DXGI_FORMAT_R10G10B10A2_TYPELESS:
case DXGI_FORMAT_R10G10B10A2_UNORM:
case DXGI_FORMAT_R10G10B10A2_UINT:
case DXGI_FORMAT_R11G11B10_FLOAT:
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_R8G8B8A8_UINT:
case DXGI_FORMAT_R8G8B8A8_SNORM:
case DXGI_FORMAT_R8G8B8A8_SINT:
case DXGI_FORMAT_R16G16_TYPELESS:
case DXGI_FORMAT_R16G16_FLOAT:
case DXGI_FORMAT_R16G16_UNORM:
case DXGI_FORMAT_R16G16_UINT:
case DXGI_FORMAT_R16G16_SNORM:
case DXGI_FORMAT_R16G16_SINT:
case DXGI_FORMAT_R32_TYPELESS:
case DXGI_FORMAT_D32_FLOAT:
case DXGI_FORMAT_R32_FLOAT:
case DXGI_FORMAT_R32_UINT:
case DXGI_FORMAT_R32_SINT:
case DXGI_FORMAT_R24G8_TYPELESS:
case DXGI_FORMAT_D24_UNORM_S8_UINT:
case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
case DXGI_FORMAT_R9G9B9E5_SHAREDEXP:
case DXGI_FORMAT_R8G8_B8G8_UNORM:
case DXGI_FORMAT_G8R8_G8B8_UNORM:
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8X8_UNORM:
case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8X8_TYPELESS:
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
return 32;
case DXGI_FORMAT_R8G8_TYPELESS:
case DXGI_FORMAT_R8G8_UNORM:
case DXGI_FORMAT_R8G8_UINT:
case DXGI_FORMAT_R8G8_SNORM:
case DXGI_FORMAT_R8G8_SINT:
case DXGI_FORMAT_R16_TYPELESS:
case DXGI_FORMAT_R16_FLOAT:
case DXGI_FORMAT_D16_UNORM:
case DXGI_FORMAT_R16_UNORM:
case DXGI_FORMAT_R16_UINT:
case DXGI_FORMAT_R16_SNORM:
case DXGI_FORMAT_R16_SINT:
case DXGI_FORMAT_B5G6R5_UNORM:
case DXGI_FORMAT_B5G5R5A1_UNORM:
return 16;
case DXGI_FORMAT_R8_TYPELESS:
case DXGI_FORMAT_R8_UNORM:
case DXGI_FORMAT_R8_UINT:
case DXGI_FORMAT_R8_SNORM:
case DXGI_FORMAT_R8_SINT:
case DXGI_FORMAT_A8_UNORM:
return 8;
case DXGI_FORMAT_R1_UNORM:
return 1;
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
return 4;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
return 8;
default:
return 0;
}
}
static bool isBlockCompressed(DXGIFormat fmt)
{
switch (fmt) switch (fmt)
{ {
case DXGI_FORMAT_BC1_TYPELESS: case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM: case DXGI_FORMAT_BC1_UNORM:
f = FORMAT_DXT1;
break;
case DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT_BC1_UNORM_SRGB:
f = FORMAT_DXT1srgb;
break;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
f = FORMAT_DXT3;
break;
case DXGI_FORMAT_BC2_UNORM_SRGB:
f = FORMAT_DXT3srgb;
break;
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
f = FORMAT_DXT5;
break;
case DXGI_FORMAT_BC3_UNORM_SRGB:
f = FORMAT_DXT5srgb;
break;
case DXGI_FORMAT_BC4_TYPELESS: case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM: case DXGI_FORMAT_BC4_UNORM:
f = FORMAT_BC4;
break;
case DXGI_FORMAT_BC4_SNORM: case DXGI_FORMAT_BC4_SNORM:
f = FORMAT_BC4s; case DXGI_FORMAT_BC2_TYPELESS:
break; case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_BC5_TYPELESS: case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM: case DXGI_FORMAT_BC5_UNORM:
f = FORMAT_BC5;
break;
case DXGI_FORMAT_BC5_SNORM: case DXGI_FORMAT_BC5_SNORM:
f = FORMAT_BC5s;
break;
case DXGI_FORMAT_BC6H_TYPELESS: case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16: case DXGI_FORMAT_BC6H_UF16:
f = FORMAT_BC6H;
break;
case DXGI_FORMAT_BC6H_SF16: case DXGI_FORMAT_BC6H_SF16:
f = FORMAT_BC6Hs;
break;
case DXGI_FORMAT_BC7_TYPELESS: case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM:
f = FORMAT_BC7;
break;
case DXGI_FORMAT_BC7_UNORM_SRGB: case DXGI_FORMAT_BC7_UNORM_SRGB:
f = FORMAT_BC7srgb; return true;
break;
default: default:
break; return false;
} }
return f;
} }
bool isDDS(const void *data, size_t dataSize) bool isDDS(const void *data, size_t dataSize)
@@ -170,10 +410,10 @@ bool isDDS(const void *data, size_t dataSize)
return true; return true;
} }
bool isCompressedDDS(const void *data, size_t dataSize) DXGIFormat getDDSPixelFormat(const void *data, size_t dataSize)
{ {
if (!isDDS(data, dataSize)) if (!isDDS(data, dataSize))
return false; return DXGI_FORMAT_UNKNOWN;
const uint8_t *readData = (const uint8_t *) data; const uint8_t *readData = (const uint8_t *) data;
ptrdiff_t offset = sizeof(uint32_t); ptrdiff_t offset = sizeof(uint32_t);
@@ -185,14 +425,20 @@ bool isCompressedDDS(const void *data, size_t dataSize)
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == MakeFourCC('D','X','1','0'))) if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == MakeFourCC('D','X','1','0')))
{ {
DDSHeader10 *header10 = (DDSHeader10 *) &readData[offset]; DDSHeader10 *header10 = (DDSHeader10 *) &readData[offset];
return parseDX10Format(header10->dxgiFormat) != FORMAT_UNKNOWN; return header10->dxgiFormat;
} }
return parseDDSFormat(header->format) != FORMAT_UNKNOWN; return getDXGIFormat(header->format);
}
bool isCompressedDDS(const void *data, size_t dataSize)
{
DXGIFormat format = getDDSPixelFormat(data, dataSize);
return format != DXGI_FORMAT_UNKNOWN && isBlockCompressed(format);
} }
Parser::Parser(const void *data, size_t dataSize) Parser::Parser(const void *data, size_t dataSize)
: format(FORMAT_UNKNOWN) : format(DXGI_FORMAT_UNKNOWN)
{ {
parseData(data, dataSize); parseData(data, dataSize);
} }
@@ -204,7 +450,7 @@ Parser::Parser(const Parser &other)
} }
Parser::Parser() Parser::Parser()
: format(FORMAT_UNKNOWN) : format(DXGI_FORMAT_UNKNOWN)
{ {
} }
@@ -220,7 +466,7 @@ Parser::~Parser()
{ {
} }
Format Parser::getFormat() const DXGIFormat Parser::getFormat() const
{ {
return format; return format;
} }
@@ -238,45 +484,78 @@ size_t Parser::getMipmapCount() const
return texData.size(); return texData.size();
} }
size_t Parser::parseImageSize(Format fmt, int width, int height) const size_t Parser::parseImageSize(DXGIFormat fmt, int width, int height) const
{ {
size_t numBlocksWide = 0; size_t bytes = 0;
size_t numBlocksHigh = 0; size_t bytesPerBlock = 0;
size_t numBytesPerBlock = 0;
bool packed = false;
bool blockCompressed = false;
switch (fmt) switch (fmt)
{ {
case FORMAT_DXT1: case DXGI_FORMAT_BC1_TYPELESS:
case FORMAT_DXT1srgb: case DXGI_FORMAT_BC1_UNORM:
case FORMAT_BC4: case DXGI_FORMAT_BC1_UNORM_SRGB:
case FORMAT_BC4s: case DXGI_FORMAT_BC4_TYPELESS:
numBytesPerBlock = 8; case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
blockCompressed = true;
bytesPerBlock = 8;
break; break;
case FORMAT_DXT3: case DXGI_FORMAT_BC2_TYPELESS:
case FORMAT_DXT3srgb: case DXGI_FORMAT_BC2_UNORM:
case FORMAT_DXT5: case DXGI_FORMAT_BC2_UNORM_SRGB:
case FORMAT_DXT5srgb: case DXGI_FORMAT_BC3_TYPELESS:
case FORMAT_BC5s: case DXGI_FORMAT_BC3_UNORM:
case FORMAT_BC5: case DXGI_FORMAT_BC3_UNORM_SRGB:
case FORMAT_BC6H: case DXGI_FORMAT_BC5_TYPELESS:
case FORMAT_BC7: case DXGI_FORMAT_BC5_UNORM:
case FORMAT_BC7srgb: case DXGI_FORMAT_BC5_SNORM:
numBytesPerBlock = 16; case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
blockCompressed = true;
bytesPerBlock = 16;
break;
case DXGI_FORMAT_R8G8_B8G8_UNORM:
case DXGI_FORMAT_G8R8_G8B8_UNORM:
packed = true;
bytesPerBlock = 4;
break; break;
default: default:
break; break;
} }
if (width > 0) if (packed)
numBlocksWide = std::max(1, (width + 3) / 4); {
size_t rowBytes = (((size_t) width + 1u) >> 1) * bytesPerBlock;
bytes = rowBytes * height;
}
else if (blockCompressed)
{
size_t numBlocksWide = width > 0 ? std::max(1, (width + 3) / 4) : 0;
size_t numBlocksHigh = height > 0 ? std::max(1, (height + 3) / 4) : 0;
bytes = numBlocksWide * bytesPerBlock * numBlocksHigh;
}
else
{
size_t bpp = getBitsPerPixel(fmt);
if (bpp == 0)
return 0;
if (height > 0) // Round up to the nearest byte.
numBlocksHigh = std::max(1, (height + 3) / 4); size_t rowBytes = ((size_t) width * bpp + 7u) / 8u;
bytes = rowBytes * height;
}
return numBlocksWide * numBytesPerBlock * numBlocksHigh; return bytes;
} }
bool Parser::parseTexData(const uint8_t *data, size_t dataSize, Format fmt, int w, int h, int mips) bool Parser::parseTexData(const uint8_t *data, size_t dataSize, DXGIFormat fmt, int w, int h, int mips)
{ {
size_t offset = 0; size_t offset = 0;
std::vector<Image> newTexData; std::vector<Image> newTexData;
@@ -321,7 +600,6 @@ bool Parser::parseData(const void *data, size_t dataSize)
DDSHeader *header = (DDSHeader *) &readData[offset]; DDSHeader *header = (DDSHeader *) &readData[offset];
offset += sizeof(DDSHeader); offset += sizeof(DDSHeader);
// Check for DX10 extension. // Check for DX10 extension.
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == MakeFourCC('D','X','1','0'))) if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == MakeFourCC('D','X','1','0')))
{ {
@@ -342,16 +620,14 @@ bool Parser::parseData(const void *data, size_t dataSize)
if (header10->arraySize > 1) if (header10->arraySize > 1)
return false; return false;
format = parseDX10Format(header10->dxgiFormat); format = header10->dxgiFormat;
} }
else else
format = parseDDSFormat(header->format); format = getDXGIFormat(header->format);
if (format == DXGI_FORMAT_UNKNOWN)
if (format == FORMAT_UNKNOWN)
return false; return false;
int w = header->width; int w = header->width;
int h = header->height; int h = header->height;
+13 -33
View File
@@ -1,7 +1,7 @@
/** /**
* Simple DDS data parser for compressed 2D textures. * Simple DDS data parser for compressed 2D textures.
* *
* Copyright (c) 2013-2017 Alex Szpakowski * Copyright (c) 2013-2019 Alex Szpakowski
* *
* This software is provided 'as-is', without any express or implied * This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages * warranty. In no event will the authors be held liable for any damages
@@ -28,40 +28,18 @@
#include <vector> #include <vector>
#include "ddsinfo.h"
namespace dds namespace dds
{ {
// Supported DDS formats.
// Formats with an 's' suffix have signed data.
enum Format
{
FORMAT_DXT1,
FORMAT_DXT1srgb,
FORMAT_DXT3,
FORMAT_DXT3srgb,
FORMAT_DXT5,
FORMAT_DXT5srgb,
FORMAT_BC4,
FORMAT_BC4s,
FORMAT_BC5,
FORMAT_BC5s,
FORMAT_BC6H,
FORMAT_BC6Hs,
FORMAT_BC7,
FORMAT_BC7srgb, // sRGB color space.
FORMAT_UNKNOWN
};
// Represents a single mipmap level of a texture. // Represents a single mipmap level of a texture.
struct Image struct Image
{ {
int width; int width = 0;
int height; int height = 0;
size_t dataSize; size_t dataSize = 0;
const uint8_t *data; const uint8_t *data = nullptr;
Image() : width(0), height(0), dataSize(0), data(0)
{}
}; };
/** /**
@@ -82,6 +60,8 @@ bool isDDS(const void *data, size_t dataSize);
**/ **/
bool isCompressedDDS(const void *data, size_t dataSize); bool isCompressedDDS(const void *data, size_t dataSize);
dxinfo::DXGIFormat getDDSPixelFormat(const void *data, size_t dataSize);
class Parser class Parser
{ {
public: public:
@@ -104,7 +84,7 @@ public:
/** /**
* Gets the format of this texture. * Gets the format of this texture.
**/ **/
Format getFormat() const; dxinfo::DXGIFormat getFormat() const;
/** /**
* Gets the data of this texture at a mipmap level. Mipmap level 0 * Gets the data of this texture at a mipmap level. Mipmap level 0
@@ -124,12 +104,12 @@ public:
private: private:
size_t parseImageSize(Format fmt, int width, int height) const; size_t parseImageSize(dxinfo::DXGIFormat fmt, int width, int height) const;
bool parseTexData(const uint8_t *data, size_t dataSize, Format fmt, int w, int h, int mips); bool parseTexData(const uint8_t *data, size_t dataSize, dxinfo::DXGIFormat fmt, int w, int h, int mips);
bool parseData(const void *data, size_t dataSize); bool parseData(const void *data, size_t dataSize);
std::vector<Image> texData; std::vector<Image> texData;
Format format; dxinfo::DXGIFormat format;
}; // Parser }; // Parser
+171 -44
View File
@@ -20,6 +20,10 @@
#include "ddsHandler.h" #include "ddsHandler.h"
#include "common/Exception.h" #include "common/Exception.h"
#include "image/ImageData.h"
// dds parser
#include "ddsparse/ddsparse.h"
namespace love namespace love
{ {
@@ -28,6 +32,172 @@ namespace image
namespace magpie namespace magpie
{ {
static PixelFormat convertFormat(dds::dxinfo::DXGIFormat dxformat, bool &sRGB)
{
using namespace dds::dxinfo;
sRGB = false;
switch (dxformat)
{
case DXGI_FORMAT_R32G32B32A32_TYPELESS:
case DXGI_FORMAT_R32G32B32A32_FLOAT:
return PIXELFORMAT_RGBA32F;
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
return PIXELFORMAT_RGBA16F;
case DXGI_FORMAT_R16G16B16A16_UNORM:
return PIXELFORMAT_RGBA16;
case DXGI_FORMAT_R32G32_TYPELESS:
case DXGI_FORMAT_R32G32_FLOAT:
return PIXELFORMAT_RG32F;
case DXGI_FORMAT_R10G10B10A2_TYPELESS:
case DXGI_FORMAT_R10G10B10A2_UNORM:
return PIXELFORMAT_RGB10A2;
case DXGI_FORMAT_R11G11B10_FLOAT:
return PIXELFORMAT_RG11B10F;
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
return PIXELFORMAT_RGBA8;
case DXGI_FORMAT_R16G16_TYPELESS:
case DXGI_FORMAT_R16G16_FLOAT:
return PIXELFORMAT_RG16F;
case DXGI_FORMAT_R16G16_UNORM:
return PIXELFORMAT_RG16;
case DXGI_FORMAT_R32_TYPELESS:
case DXGI_FORMAT_R32_FLOAT:
return PIXELFORMAT_R32F;
case DXGI_FORMAT_R8G8_TYPELESS:
case DXGI_FORMAT_R8G8_UNORM:
return PIXELFORMAT_RG8;
case DXGI_FORMAT_R16_TYPELESS:
case DXGI_FORMAT_R16_FLOAT:
return PIXELFORMAT_R16F;
case DXGI_FORMAT_R16_UNORM:
return PIXELFORMAT_R16;
case DXGI_FORMAT_R8_TYPELESS:
case DXGI_FORMAT_R8_UNORM:
case DXGI_FORMAT_A8_UNORM:
return PIXELFORMAT_R8;
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC1_UNORM_SRGB);
return PIXELFORMAT_DXT1;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC2_UNORM_SRGB);
return PIXELFORMAT_DXT3;
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC3_UNORM_SRGB);
return PIXELFORMAT_DXT5;
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
return PIXELFORMAT_BC4;
case DXGI_FORMAT_BC4_SNORM:
return PIXELFORMAT_BC4s;
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
return PIXELFORMAT_BC5;
case DXGI_FORMAT_BC5_SNORM:
return PIXELFORMAT_BC5s;
case DXGI_FORMAT_B5G6R5_UNORM:
return PIXELFORMAT_RGB565;
case DXGI_FORMAT_B5G5R5A1_UNORM:
return PIXELFORMAT_RGB5A1;
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
return PIXELFORMAT_BC6H;
case DXGI_FORMAT_BC6H_SF16:
return PIXELFORMAT_BC6Hs;
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
sRGB = (dxformat == DXGI_FORMAT_BC7_UNORM_SRGB);
return PIXELFORMAT_BC7;
default:
return PIXELFORMAT_UNKNOWN;
}
}
bool DDSHandler::canDecode(Data *data)
{
using namespace dds::dxinfo;
DXGIFormat dxformat = dds::getDDSPixelFormat(data->getData(), data->getSize());
bool isSRGB = false;
PixelFormat format = convertFormat(dxformat, isSRGB);
return ImageData::validPixelFormat(format);
}
FormatHandler::DecodedImage DDSHandler::decode(Data *data)
{
DecodedImage img;
dds::Parser parser(data->getData(), data->getSize());
bool isSRGB = false;
img.format = convertFormat(parser.getFormat(), isSRGB);
if (!ImageData::validPixelFormat(img.format))
throw love::Exception("Could not parse DDS pixel data: Unsupported format.");
if (parser.getMipmapCount() == 0)
throw love::Exception("Could not parse DDS pixel data: No readable texture data.");
// We only support the top mip level through this codepath.
const dds::Image *ddsimg = parser.getImageData(0);
try
{
img.data = new uint8[ddsimg->dataSize];
}
catch (std::exception &)
{
throw love::Exception("Out of memory.");
}
memcpy(img.data, ddsimg->data, ddsimg->dataSize);
img.size = ddsimg->dataSize;
img.width = ddsimg->width;
img.height = ddsimg->height;
return img;
}
bool DDSHandler::canParseCompressed(Data *data) bool DDSHandler::canParseCompressed(Data *data)
{ {
return dds::isCompressedDDS(data->getData(), data->getSize()); return dds::isCompressedDDS(data->getData(), data->getSize());
@@ -35,7 +205,7 @@ bool DDSHandler::canParseCompressed(Data *data)
StrongRef<CompressedMemory> DDSHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB) StrongRef<CompressedMemory> DDSHandler::parseCompressed(Data *filedata, std::vector<StrongRef<CompressedSlice>> &images, PixelFormat &format, bool &sRGB)
{ {
if (!dds::isDDS(filedata->getData(), filedata->getSize())) if (!dds::isCompressedDDS(filedata->getData(), filedata->getSize()))
throw love::Exception("Could not decode compressed data (not a DDS file?)"); throw love::Exception("Could not decode compressed data (not a DDS file?)");
PixelFormat texformat = PIXELFORMAT_UNKNOWN; PixelFormat texformat = PIXELFORMAT_UNKNOWN;
@@ -88,49 +258,6 @@ StrongRef<CompressedMemory> DDSHandler::parseCompressed(Data *filedata, std::vec
return memory; return memory;
} }
PixelFormat DDSHandler::convertFormat(dds::Format ddsformat, bool &sRGB)
{
sRGB = false;
switch (ddsformat)
{
case dds::FORMAT_DXT1:
return PIXELFORMAT_DXT1;
case dds::FORMAT_DXT1srgb:
sRGB = true;
return PIXELFORMAT_DXT1;
case dds::FORMAT_DXT3:
return PIXELFORMAT_DXT3;
case dds::FORMAT_DXT3srgb:
sRGB = true;
return PIXELFORMAT_DXT3;
case dds::FORMAT_DXT5:
return PIXELFORMAT_DXT5;
case dds::FORMAT_DXT5srgb:
sRGB = true;
return PIXELFORMAT_DXT5;
case dds::FORMAT_BC4:
return PIXELFORMAT_BC4;
case dds::FORMAT_BC4s:
return PIXELFORMAT_BC4s;
case dds::FORMAT_BC5:
return PIXELFORMAT_BC5;
case dds::FORMAT_BC5s:
return PIXELFORMAT_BC5s;
case dds::FORMAT_BC6H:
return PIXELFORMAT_BC6H;
case dds::FORMAT_BC6Hs:
return PIXELFORMAT_BC6Hs;
case dds::FORMAT_BC7:
return PIXELFORMAT_BC7;
case dds::FORMAT_BC7srgb:
sRGB = true;
return PIXELFORMAT_BC7;
default:
return PIXELFORMAT_UNKNOWN;
}
}
} // magpie } // magpie
} // image } // image
} // love } // love
+2 -8
View File
@@ -23,9 +23,6 @@
// LOVE // LOVE
#include "image/FormatHandler.h" #include "image/FormatHandler.h"
// dds parser
#include "ddsparse/ddsparse.h"
// STL // STL
#include <string> #include <string>
@@ -46,16 +43,13 @@ public:
virtual ~DDSHandler() {} virtual ~DDSHandler() {}
// Implements FormatHandler. // Implements FormatHandler.
bool canDecode(Data *data) override;
DecodedImage decode(Data *data) override;
bool canParseCompressed(Data *data) override; bool canParseCompressed(Data *data) override;
StrongRef<CompressedMemory> parseCompressed(Data *filedata, StrongRef<CompressedMemory> parseCompressed(Data *filedata,
std::vector<StrongRef<CompressedSlice>> &images, std::vector<StrongRef<CompressedSlice>> &images,
PixelFormat &format, bool &sRGB) override; PixelFormat &format, bool &sRGB) override;
private:
static PixelFormat convertFormat(dds::Format ddsformat, bool &sRGB);
}; // DDSHandler }; // DDSHandler
} // magpie } // magpie