mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Removed support for BC7-compressed DDS images (only *very* modern systems support it right now); updated the ddsparse library
This commit is contained in:
+141
-143
@@ -33,155 +33,50 @@ namespace dds
|
||||
using namespace dds::dxinfo;
|
||||
|
||||
// Creates a packed uint representation of a FourCC code.
|
||||
static inline uint32_t FourCC(char a, char b, char c, char d)
|
||||
#define MakeFourCC(a, b, c, d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)))
|
||||
|
||||
// Translate the old DDS format to our own.
|
||||
static Format parseDDSFormat(const DDSPixelFormat &fmt)
|
||||
{
|
||||
uint32_t fcc = ((uint32_t) a)
|
||||
| (((uint32_t) b) << 8)
|
||||
| (((uint32_t) c) << 16)
|
||||
| (((uint32_t) d) << 24);
|
||||
if ((fmt.flags & DDPF_FOURCC) == 0)
|
||||
return FORMAT_UNKNOWN;
|
||||
|
||||
return fcc;
|
||||
}
|
||||
Format f = FORMAT_UNKNOWN;
|
||||
|
||||
Parser::Image::Image()
|
||||
: width(0)
|
||||
, height(0)
|
||||
, dataSize(0)
|
||||
, data(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool Parser::isDDS(const void *data, size_t dataSize)
|
||||
{
|
||||
const uint8_t *readData = (const uint8_t *) data;
|
||||
ptrdiff_t offset = 0;
|
||||
|
||||
// Is the data large enough to hold the DDS header?
|
||||
if(dataSize < sizeof(uint32_t) + sizeof(DDSHeader))
|
||||
return false;
|
||||
|
||||
// All DDS files start with "DDS ".
|
||||
if((*(uint32_t *) readData) != FourCC('D','D','S',' '))
|
||||
return false;
|
||||
|
||||
offset += sizeof(uint32_t);
|
||||
|
||||
DDSHeader *header = (DDSHeader *) &readData[offset];
|
||||
|
||||
|
||||
// Verify header to validate DDS data.
|
||||
if (header->size != sizeof(DDSHeader) || header->format.size != sizeof(DDSPixelFormat))
|
||||
return false;
|
||||
|
||||
offset += sizeof(DDSHeader);
|
||||
|
||||
// Check for DX10 extension.
|
||||
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FourCC('D','X','1','0')))
|
||||
switch (fmt.fourCC)
|
||||
{
|
||||
// Data must be big enough for both headers plus the magic value.
|
||||
if (dataSize < (sizeof(uint32_t) + sizeof(DDSHeader) + sizeof(DDSHeader10)))
|
||||
return false;
|
||||
case MakeFourCC('D','X','T','1'):
|
||||
f = FORMAT_DXT1;
|
||||
break;
|
||||
case MakeFourCC('D','X','T','3'):
|
||||
f = FORMAT_DXT3;
|
||||
break;
|
||||
case MakeFourCC('D','X','T','5'):
|
||||
f = FORMAT_DXT5;
|
||||
break;
|
||||
case MakeFourCC('A','T','I','1'):
|
||||
case MakeFourCC('B','C','4','U'):
|
||||
f = FORMAT_BC4;
|
||||
break;
|
||||
case MakeFourCC('B','C','4','S'):
|
||||
f = FORMAT_BC4s;
|
||||
break;
|
||||
case MakeFourCC('A','T','I','2'):
|
||||
case MakeFourCC('B','C','5','U'):
|
||||
f = FORMAT_BC5;
|
||||
break;
|
||||
case MakeFourCC('B','C','5','S'):
|
||||
f = FORMAT_BC5s;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
return f;
|
||||
}
|
||||
|
||||
bool Parser::isCompressedDDS(const void *data, size_t dataSize)
|
||||
{
|
||||
if (!isDDS(data, dataSize))
|
||||
return false;
|
||||
|
||||
const uint8_t *readData = (const uint8_t *) data;
|
||||
ptrdiff_t offset = sizeof(uint32_t);
|
||||
|
||||
DDSHeader *header = (DDSHeader *) &readData[offset];
|
||||
offset += sizeof(DDSHeader);
|
||||
|
||||
// Check for DX10 extension.
|
||||
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FourCC('D','X','1','0')))
|
||||
{
|
||||
DDSHeader10 *header10 = (DDSHeader10 *) &readData[offset];
|
||||
return parseDX10Format(header10->dxgiFormat) != FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
return parseDDSFormat(header->format) != FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
Parser::Parser(const void *data, size_t dataSize)
|
||||
: format(FORMAT_UNKNOWN)
|
||||
{
|
||||
parseData(data, dataSize);
|
||||
}
|
||||
|
||||
Parser::Parser(const Parser &other)
|
||||
: texData(other.texData)
|
||||
, format(other.format)
|
||||
{
|
||||
}
|
||||
|
||||
Parser::Parser()
|
||||
: format(FORMAT_UNKNOWN)
|
||||
{
|
||||
}
|
||||
|
||||
Parser &Parser::operator = (const Parser &other)
|
||||
{
|
||||
texData = other.texData;
|
||||
format = other.format;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Parser::~Parser()
|
||||
{
|
||||
}
|
||||
|
||||
Format Parser::getFormat() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
const Parser::Image *Parser::getImageData(size_t miplevel) const
|
||||
{
|
||||
if (miplevel >= texData.size())
|
||||
return 0;
|
||||
|
||||
return &texData[miplevel];
|
||||
}
|
||||
|
||||
size_t Parser::getNumMipmaps() const
|
||||
{
|
||||
return texData.size();
|
||||
}
|
||||
|
||||
Format Parser::parseDDSFormat(const DDSPixelFormat &fmt)
|
||||
{
|
||||
if (fmt.flags & DDPF_FOURCC)
|
||||
{
|
||||
if (fmt.fourCC == FourCC('D','X','T','1'))
|
||||
return FORMAT_DXT1;
|
||||
else if (fmt.fourCC == FourCC('D','X','T','3'))
|
||||
return FORMAT_DXT3;
|
||||
else if (fmt.fourCC == FourCC('D','X','T','5'))
|
||||
return FORMAT_DXT5;
|
||||
else if (fmt.fourCC == FourCC('A','T','I','1'))
|
||||
return FORMAT_BC4;
|
||||
else if (fmt.fourCC == FourCC('A','T','I','2'))
|
||||
return FORMAT_BC5;
|
||||
else if (fmt.fourCC == FourCC('B','C','4','U'))
|
||||
return FORMAT_BC4;
|
||||
else if (fmt.fourCC == FourCC('B','C','4','S'))
|
||||
return FORMAT_BC4s;
|
||||
else if (fmt.fourCC == FourCC('B','C','5','U'))
|
||||
return FORMAT_BC5;
|
||||
else if (fmt.fourCC == FourCC('B','C','5','S'))
|
||||
return FORMAT_BC5s;
|
||||
}
|
||||
|
||||
return FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
Format Parser::parseDX10Format(DXGIFormat fmt)
|
||||
// Translate the new DX10 formats to our own.
|
||||
static Format parseDX10Format(DXGIFormat fmt)
|
||||
{
|
||||
Format f = FORMAT_UNKNOWN;
|
||||
|
||||
@@ -233,10 +128,113 @@ Format Parser::parseDX10Format(DXGIFormat fmt)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
bool isDDS(const void *data, size_t dataSize)
|
||||
{
|
||||
const uint8_t *readData = (const uint8_t *) data;
|
||||
ptrdiff_t offset = 0;
|
||||
|
||||
// Is the data large enough to hold the DDS header?
|
||||
if(dataSize < sizeof(uint32_t) + sizeof(DDSHeader))
|
||||
return false;
|
||||
|
||||
// All DDS files start with "DDS ".
|
||||
if((*(uint32_t *) readData) != MakeFourCC('D','D','S',' '))
|
||||
return false;
|
||||
|
||||
offset += sizeof(uint32_t);
|
||||
|
||||
DDSHeader *header = (DDSHeader *) &readData[offset];
|
||||
|
||||
|
||||
// Verify header to validate DDS data.
|
||||
if (header->size != sizeof(DDSHeader) || header->format.size != sizeof(DDSPixelFormat))
|
||||
return false;
|
||||
|
||||
offset += sizeof(DDSHeader);
|
||||
|
||||
// Check for DX10 extension.
|
||||
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == MakeFourCC('D','X','1','0')))
|
||||
{
|
||||
// Data must be big enough for both headers plus the magic value.
|
||||
if (dataSize < (sizeof(uint32_t) + sizeof(DDSHeader) + sizeof(DDSHeader10)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isCompressedDDS(const void *data, size_t dataSize)
|
||||
{
|
||||
if (!isDDS(data, dataSize))
|
||||
return false;
|
||||
|
||||
const uint8_t *readData = (const uint8_t *) data;
|
||||
ptrdiff_t offset = sizeof(uint32_t);
|
||||
|
||||
DDSHeader *header = (DDSHeader *) &readData[offset];
|
||||
offset += sizeof(DDSHeader);
|
||||
|
||||
// Check for DX10 extension.
|
||||
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == MakeFourCC('D','X','1','0')))
|
||||
{
|
||||
DDSHeader10 *header10 = (DDSHeader10 *) &readData[offset];
|
||||
return parseDX10Format(header10->dxgiFormat) != FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
return parseDDSFormat(header->format) != FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
Parser::Parser(const void *data, size_t dataSize)
|
||||
: format(FORMAT_UNKNOWN)
|
||||
{
|
||||
parseData(data, dataSize);
|
||||
}
|
||||
|
||||
Parser::Parser(const Parser &other)
|
||||
: texData(other.texData)
|
||||
, format(other.format)
|
||||
{
|
||||
}
|
||||
|
||||
Parser::Parser()
|
||||
: format(FORMAT_UNKNOWN)
|
||||
{
|
||||
}
|
||||
|
||||
Parser &Parser::operator = (const Parser &other)
|
||||
{
|
||||
texData = other.texData;
|
||||
format = other.format;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Parser::~Parser()
|
||||
{
|
||||
}
|
||||
|
||||
Format Parser::getFormat() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
const Image *Parser::getImageData(size_t miplevel) const
|
||||
{
|
||||
if (miplevel >= texData.size())
|
||||
return 0;
|
||||
|
||||
return &texData[miplevel];
|
||||
}
|
||||
|
||||
size_t Parser::getMipmapCount() const
|
||||
{
|
||||
return texData.size();
|
||||
}
|
||||
|
||||
size_t Parser::parseImageSize(Format fmt, int width, int height) const
|
||||
{
|
||||
size_t size = 0;
|
||||
@@ -318,7 +316,7 @@ bool Parser::parseData(const void *data, size_t dataSize)
|
||||
|
||||
|
||||
// Check for DX10 extension.
|
||||
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FourCC('D','X','1','0')))
|
||||
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == MakeFourCC('D','X','1','0')))
|
||||
{
|
||||
DDSHeader10 *header10 = (DDSHeader10 *) &readData[offset];
|
||||
offset += sizeof(DDSHeader10);
|
||||
|
||||
@@ -25,13 +25,10 @@
|
||||
#ifndef DDS_PARSE_H
|
||||
#define DDS_PARSE_H
|
||||
|
||||
#include "ddsinfo.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
|
||||
namespace dds
|
||||
{
|
||||
@@ -54,39 +51,40 @@ enum Format
|
||||
FORMAT_UNKNOWN
|
||||
};
|
||||
|
||||
// Represents a single mipmap level of a texture.
|
||||
struct Image
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
size_t dataSize;
|
||||
const uint8_t *data;
|
||||
|
||||
Image() : width(0), height(0), dataSize(0), data(0)
|
||||
{};
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the input byte data represents a valid DDS file.
|
||||
* Does not take into account whether the texture format is supported.
|
||||
*
|
||||
* @param data The byte data to parse.
|
||||
* @param dataSize The size in bytes of the data.
|
||||
**/
|
||||
bool isDDS(const void *data, size_t dataSize);
|
||||
|
||||
/**
|
||||
* Determines whether the input byte data represents a valid compressed DDS
|
||||
* file. Takes into account texture format, but not type (3D textures, etc.)
|
||||
*
|
||||
* @param data The byte data to parse.
|
||||
* @param dataSize The size in bytes of the data.
|
||||
**/
|
||||
bool isCompressedDDS(const void *data, size_t dataSize);
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
|
||||
// Represents a single mipmap level of a texture.
|
||||
struct Image
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
size_t dataSize;
|
||||
const uint8_t *data;
|
||||
|
||||
Image();
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the input byte data represents a valid DDS file.
|
||||
* Does not take into account whether the texture format is supported.
|
||||
*
|
||||
* @param data The byte data to parse.
|
||||
* @param dataSize The size in bytes of the data.
|
||||
**/
|
||||
static bool isDDS(const void *data, size_t dataSize);
|
||||
|
||||
/**
|
||||
* Determines whether the input byte data represents a valid compressed DDS
|
||||
* file. Takes into account texture format, but not type (3D textures, etc.)
|
||||
*
|
||||
* @param data The byte data to parse.
|
||||
* @param dataSize The size in bytes of the data.
|
||||
**/
|
||||
static bool isCompressedDDS(const void *data, size_t dataSize);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Attempts to parse byte data as a compressed DDS file.
|
||||
@@ -121,17 +119,12 @@ public:
|
||||
* Gets the number of mipmap levels in this texture.
|
||||
* Includes the base mip level.
|
||||
**/
|
||||
size_t getNumMipmaps() const;
|
||||
size_t getMipmapCount() const;
|
||||
|
||||
private:
|
||||
|
||||
static Format parseDDSFormat(const dxinfo::DDSPixelFormat &fmt);
|
||||
static Format parseDX10Format(dxinfo::DXGIFormat fmt);
|
||||
|
||||
size_t parseImageSize(Format fmt, int width, int height) const;
|
||||
|
||||
bool parseTexData(const uint8_t *data, size_t dataSize, Format fmt, int w, int h, int mips);
|
||||
|
||||
bool parseData(const void *data, size_t dataSize);
|
||||
|
||||
std::vector<Image> texData;
|
||||
|
||||
@@ -178,7 +178,6 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
|
||||
{ "mipmap", Graphics::SUPPORT_MIPMAP },
|
||||
{ "dxt", Graphics::SUPPORT_DXT },
|
||||
{ "bc5", Graphics::SUPPORT_BC5 },
|
||||
{ "bc7", Graphics::SUPPORT_BC7 },
|
||||
};
|
||||
|
||||
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
|
||||
|
||||
@@ -97,7 +97,6 @@ public:
|
||||
SUPPORT_MIPMAP,
|
||||
SUPPORT_DXT,
|
||||
SUPPORT_BC5,
|
||||
SUPPORT_BC7,
|
||||
SUPPORT_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -569,10 +569,6 @@ GLenum Image::getCompressedFormat(image::CompressedData::TextureType type) const
|
||||
return GL_COMPRESSED_RG_RGTC2;
|
||||
case image::CompressedData::TYPE_BC5s:
|
||||
return GL_COMPRESSED_SIGNED_RG_RGTC2;
|
||||
case image::CompressedData::TYPE_BC7:
|
||||
return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
case image::CompressedData::TYPE_BC7srgb:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB;
|
||||
default:
|
||||
return GL_RGBA8;
|
||||
}
|
||||
@@ -614,15 +610,9 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::TextureType type)
|
||||
case image::CompressedData::TYPE_DXT3:
|
||||
case image::CompressedData::TYPE_DXT5:
|
||||
return GLEE_EXT_texture_compression_s3tc;
|
||||
|
||||
case image::CompressedData::TYPE_BC5:
|
||||
case image::CompressedData::TYPE_BC5s:
|
||||
return (GLEE_VERSION_3_0 || GLEE_ARB_texture_compression_rgtc || GLEE_EXT_texture_compression_rgtc);
|
||||
|
||||
case image::CompressedData::TYPE_BC7:
|
||||
case image::CompressedData::TYPE_BC7srgb:
|
||||
return (GLEE_VERSION_4_2 || GLEE_ARB_texture_compression_bptc);
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1068,10 +1068,6 @@ int w_isSupported(lua_State *L)
|
||||
if (!Image::hasCompressedTextureSupport(image::CompressedData::TYPE_BC5))
|
||||
supported = false;
|
||||
break;
|
||||
case Graphics::SUPPORT_BC7:
|
||||
if (!Image::hasCompressedTextureSupport(image::CompressedData::TYPE_BC7))
|
||||
supported = false;
|
||||
break;
|
||||
default:
|
||||
supported = false;
|
||||
}
|
||||
|
||||
@@ -117,8 +117,6 @@ StringMap<CompressedData::TextureType, CompressedData::TYPE_MAX_ENUM>::Entry Com
|
||||
{"dxt5", CompressedData::TYPE_DXT5},
|
||||
{"bc5", CompressedData::TYPE_BC5},
|
||||
{"bc5s", CompressedData::TYPE_BC5s},
|
||||
{"bc7", CompressedData::TYPE_BC7},
|
||||
{"bc7srgb", CompressedData::TYPE_BC7srgb},
|
||||
};
|
||||
|
||||
StringMap<CompressedData::TextureType, CompressedData::TYPE_MAX_ENUM> CompressedData::types(CompressedData::typeEntries, sizeof(CompressedData::typeEntries));
|
||||
|
||||
@@ -36,10 +36,11 @@ namespace love
|
||||
namespace image
|
||||
{
|
||||
|
||||
// CompressedData represents image data which is designed to be uploaded to the
|
||||
// GPU and rendered in its compressed form, without being un-compressed.
|
||||
// http://renderingpipeline.com/2012/07/texture-compression/
|
||||
|
||||
/**
|
||||
* CompressedData represents image data which is designed to be uploaded to the
|
||||
* GPU and rendered in its compressed form, without being un-compressed.
|
||||
* http://renderingpipeline.com/2012/07/texture-compression/
|
||||
**/
|
||||
class CompressedData : public Data
|
||||
{
|
||||
public:
|
||||
@@ -53,8 +54,6 @@ public:
|
||||
TYPE_DXT5,
|
||||
TYPE_BC5,
|
||||
TYPE_BC5s,
|
||||
TYPE_BC7,
|
||||
TYPE_BC7srgb,
|
||||
TYPE_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ bool ddsHandler::canParse(const filesystem::FileData *data)
|
||||
if (data->getExtension().compare("dds") != 0)
|
||||
return false;
|
||||
|
||||
return dds::Parser::isCompressedDDS(data->getData(), data->getSize());
|
||||
return dds::isCompressedDDS(data->getData(), data->getSize());
|
||||
}
|
||||
|
||||
CompressedData::TextureType ddsHandler::parse(filesystem::FileData *data, std::vector<CompressedData::SubImage> &images)
|
||||
{
|
||||
if (!dds::Parser::isDDS(data->getData(), data->getSize()))
|
||||
if (!dds::isDDS(data->getData(), data->getSize()))
|
||||
throw love::Exception("Could not decode compressed data (not a DDS file?)");
|
||||
|
||||
CompressedData::TextureType textype = CompressedData::TYPE_UNKNOWN;
|
||||
@@ -51,12 +51,12 @@ CompressedData::TextureType ddsHandler::parse(filesystem::FileData *data, std::v
|
||||
if (textype == CompressedData::TYPE_UNKNOWN)
|
||||
throw love::Exception("Could not parse compressed data: Unsupported format.");
|
||||
|
||||
if (parser.getNumMipmaps() == 0)
|
||||
if (parser.getMipmapCount() == 0)
|
||||
throw love::Exception("Could not parse compressed data: No readable texture data.");
|
||||
|
||||
for (size_t i = 0; i < parser.getNumMipmaps(); i++)
|
||||
for (size_t i = 0; i < parser.getMipmapCount(); i++)
|
||||
{
|
||||
const dds::Parser::Image *img = parser.getImageData(i);
|
||||
const dds::Image *img = parser.getImageData(i);
|
||||
|
||||
CompressedData::SubImage mip;
|
||||
|
||||
@@ -90,10 +90,6 @@ CompressedData::TextureType ddsHandler::convertFormat(dds::Format ddsformat)
|
||||
return CompressedData::TYPE_BC5;
|
||||
case dds::FORMAT_BC5s:
|
||||
return CompressedData::TYPE_BC5s;
|
||||
case dds::FORMAT_BC7:
|
||||
return CompressedData::TYPE_BC7;
|
||||
case dds::FORMAT_BC7srgb:
|
||||
return CompressedData::TYPE_BC7srgb;
|
||||
default:
|
||||
return CompressedData::TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define LOVE_IMAGE_MAGPIE_DDS_HANDLER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/EnumMap.h"
|
||||
#include "filesystem/FileData.h"
|
||||
#include "image/CompressedData.h"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user