mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Added support for ETC1 and PVR1 compressed textures contained in KTX, PKM, and PVR files.
Most desktop graphics drivers don't support those formats. --HG-- branch : minor
This commit is contained in:
@@ -348,6 +348,7 @@ set(LOVE_SRC_MODULE_IMAGE_ROOT
|
||||
set(LOVE_SRC_MODULE_IMAGE_MAGPIE
|
||||
src/modules/image/magpie/CompressedData.cpp
|
||||
src/modules/image/magpie/CompressedData.h
|
||||
src/modules/image/magpie/CompressedFormatHandler.h
|
||||
src/modules/image/magpie/ddsHandler.cpp
|
||||
src/modules/image/magpie/ddsHandler.h
|
||||
src/modules/image/magpie/FormatHandler.cpp
|
||||
@@ -358,8 +359,14 @@ set(LOVE_SRC_MODULE_IMAGE_MAGPIE
|
||||
src/modules/image/magpie/ImageData.h
|
||||
src/modules/image/magpie/JPEGHandler.cpp
|
||||
src/modules/image/magpie/JPEGHandler.h
|
||||
src/modules/image/magpie/KTXHandler.cpp
|
||||
src/modules/image/magpie/KTXHandler.h
|
||||
src/modules/image/magpie/PKMHandler.cpp
|
||||
src/modules/image/magpie/PKMHandler.h
|
||||
src/modules/image/magpie/PNGHandler.cpp
|
||||
src/modules/image/magpie/PNGHandler.h
|
||||
src/modules/image/magpie/PVRHandler.cpp
|
||||
src/modules/image/magpie/PVRHandler.h
|
||||
src/modules/image/magpie/STBHandler.cpp
|
||||
src/modules/image/magpie/STBHandler.h
|
||||
)
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ Released: N/A
|
||||
* Added love.graphics.getSupported (replaces love.graphics.isSupported.)
|
||||
* Added love.graphics.getSystemLimits (replaces love.graphics.getSystemLimit.)
|
||||
* Added love.graphics.stencil and love.graphics.setStencilTest (replaces love.graphics.setStencil.)
|
||||
* Added optional ClearType argument to love.graphics.clear. Current enums are "all" (the default) and "stencil".
|
||||
* Added optional type argument to love.graphics.clear. Current type enums are "all" (the default) and "stencil".
|
||||
* Added optional x/y/width/height arguments to Image:refresh.
|
||||
* Added BMFont bitmap font file support to love.graphics.newFont and love.font.
|
||||
|
||||
|
||||
@@ -272,6 +272,13 @@
|
||||
FA636D8B171B70920065623F /* RandomGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = FA636D89171B70920065623F /* RandomGenerator.h */; };
|
||||
FA636D8E171B72A70065623F /* wrap_RandomGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA636D8C171B72A70065623F /* wrap_RandomGenerator.cpp */; };
|
||||
FA636D8F171B72A70065623F /* wrap_RandomGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = FA636D8D171B72A70065623F /* wrap_RandomGenerator.h */; };
|
||||
FA6F55761A6E261800062204 /* KTXHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6F55701A6E261800062204 /* KTXHandler.cpp */; };
|
||||
FA6F55771A6E261800062204 /* KTXHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA6F55711A6E261800062204 /* KTXHandler.h */; };
|
||||
FA6F55781A6E261800062204 /* PKMHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6F55721A6E261800062204 /* PKMHandler.cpp */; };
|
||||
FA6F55791A6E261800062204 /* PKMHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA6F55731A6E261800062204 /* PKMHandler.h */; };
|
||||
FA6F557A1A6E261800062204 /* PVRHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6F55741A6E261800062204 /* PVRHandler.cpp */; };
|
||||
FA6F557B1A6E261800062204 /* PVRHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA6F55751A6E261800062204 /* PVRHandler.h */; };
|
||||
FA6F557D1A6E28A200062204 /* CompressedFormatHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA6F557C1A6E28A200062204 /* CompressedFormatHandler.h */; };
|
||||
FA70573C194A3FC600670B30 /* DroppedFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA70573A194A3FC600670B30 /* DroppedFile.cpp */; };
|
||||
FA70573D194A3FC600670B30 /* DroppedFile.h in Headers */ = {isa = PBXBuildFile; fileRef = FA70573B194A3FC600670B30 /* DroppedFile.h */; };
|
||||
FA705740194A55CB00670B30 /* wrap_DroppedFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA70573E194A55CB00670B30 /* wrap_DroppedFile.cpp */; };
|
||||
@@ -849,6 +856,13 @@
|
||||
FA636D89171B70920065623F /* RandomGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RandomGenerator.h; sourceTree = "<group>"; };
|
||||
FA636D8C171B72A70065623F /* wrap_RandomGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_RandomGenerator.cpp; sourceTree = "<group>"; };
|
||||
FA636D8D171B72A70065623F /* wrap_RandomGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_RandomGenerator.h; sourceTree = "<group>"; };
|
||||
FA6F55701A6E261800062204 /* KTXHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KTXHandler.cpp; sourceTree = "<group>"; };
|
||||
FA6F55711A6E261800062204 /* KTXHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTXHandler.h; sourceTree = "<group>"; };
|
||||
FA6F55721A6E261800062204 /* PKMHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PKMHandler.cpp; sourceTree = "<group>"; };
|
||||
FA6F55731A6E261800062204 /* PKMHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PKMHandler.h; sourceTree = "<group>"; };
|
||||
FA6F55741A6E261800062204 /* PVRHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRHandler.cpp; sourceTree = "<group>"; };
|
||||
FA6F55751A6E261800062204 /* PVRHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRHandler.h; sourceTree = "<group>"; };
|
||||
FA6F557C1A6E28A200062204 /* CompressedFormatHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressedFormatHandler.h; sourceTree = "<group>"; };
|
||||
FA70573A194A3FC600670B30 /* DroppedFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DroppedFile.cpp; sourceTree = "<group>"; };
|
||||
FA70573B194A3FC600670B30 /* DroppedFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DroppedFile.h; sourceTree = "<group>"; };
|
||||
FA70573E194A55CB00670B30 /* wrap_DroppedFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_DroppedFile.cpp; sourceTree = "<group>"; };
|
||||
@@ -1124,6 +1138,7 @@
|
||||
children = (
|
||||
FAEC808C1711E76C0057279A /* CompressedData.cpp */,
|
||||
FAEC808D1711E76C0057279A /* CompressedData.h */,
|
||||
FA6F557C1A6E28A200062204 /* CompressedFormatHandler.h */,
|
||||
FAE010DE170DE25E006F29D0 /* ddsHandler.cpp */,
|
||||
FAE010DF170DE25E006F29D0 /* ddsHandler.h */,
|
||||
FADD58DC18C30367005FC3BF /* FormatHandler.cpp */,
|
||||
@@ -1134,8 +1149,14 @@
|
||||
FAEC80891710FEA60057279A /* ImageData.h */,
|
||||
FA48E43418F10D00007CF0BD /* JPEGHandler.cpp */,
|
||||
FA48E43518F10D00007CF0BD /* JPEGHandler.h */,
|
||||
FA6F55701A6E261800062204 /* KTXHandler.cpp */,
|
||||
FA6F55711A6E261800062204 /* KTXHandler.h */,
|
||||
FA6F55721A6E261800062204 /* PKMHandler.cpp */,
|
||||
FA6F55731A6E261800062204 /* PKMHandler.h */,
|
||||
FA4BBCE018A5DD7E0027707D /* PNGHandler.cpp */,
|
||||
FA4BBCE118A5DD7E0027707D /* PNGHandler.h */,
|
||||
FA6F55741A6E261800062204 /* PVRHandler.cpp */,
|
||||
FA6F55751A6E261800062204 /* PVRHandler.h */,
|
||||
FADE620719368D5C00C25B97 /* STBHandler.cpp */,
|
||||
FADE620819368D5C00C25B97 /* STBHandler.h */,
|
||||
);
|
||||
@@ -2052,6 +2073,7 @@
|
||||
FAF6705018184FF800DBDEEA /* wuff_convert.h in Headers */,
|
||||
FAF272AB16E3D44400CC193A /* wrap_Channel.h in Headers */,
|
||||
FA0A4BF9182E260200E1E4D2 /* wrap_MotorJoint.h in Headers */,
|
||||
FA6F557D1A6E28A200062204 /* CompressedFormatHandler.h in Headers */,
|
||||
FAF272AD16E3D44400CC193A /* wrap_LuaThread.h in Headers */,
|
||||
FAF272AF16E3D44400CC193A /* wrap_ThreadModule.h in Headers */,
|
||||
FAF272B616E3D46400CC193A /* Thread.h in Headers */,
|
||||
@@ -2072,11 +2094,13 @@
|
||||
FAEC808F1711E76C0057279A /* CompressedData.h in Headers */,
|
||||
FA0F0D5B19A2CFEB0010A75B /* glad.hpp in Headers */,
|
||||
FA636D8B171B70920065623F /* RandomGenerator.h in Headers */,
|
||||
FA6F557B1A6E261800062204 /* PVRHandler.h in Headers */,
|
||||
FA636D8F171B72A70065623F /* wrap_RandomGenerator.h in Headers */,
|
||||
FAC86E641724552C00EED715 /* wrap_Quad.h in Headers */,
|
||||
FAC86E6C1724555D00EED715 /* Quad.h in Headers */,
|
||||
FA03546D1731F3A700284828 /* simplexnoise1234.h in Headers */,
|
||||
FA3D9E0E16E68DE600CA6630 /* Cursor.h in Headers */,
|
||||
FA6F55771A6E261800062204 /* KTXHandler.h in Headers */,
|
||||
FA3D9E1216E68EAE00CA6630 /* Cursor.h in Headers */,
|
||||
FA3D9E1616E6D41100CA6630 /* wrap_Cursor.h in Headers */,
|
||||
FA9FC0B1173D6E3E005027FF /* wrap_Window.h in Headers */,
|
||||
@@ -2104,6 +2128,7 @@
|
||||
FA5FDC861788D548002F0ED2 /* win32.h in Headers */,
|
||||
FA5FDC8F1788D548002F0ED2 /* lua-enet.h in Headers */,
|
||||
FA7175AA178E8418001FE7FE /* lua.h in Headers */,
|
||||
FA6F55791A6E261800062204 /* PKMHandler.h in Headers */,
|
||||
FA34E7CE174B513F00E04D3F /* System.h in Headers */,
|
||||
FA34E7D3174B515D00E04D3F /* System.h in Headers */,
|
||||
FAF670571818501300DBDEEA /* WaveDecoder.h in Headers */,
|
||||
@@ -2182,6 +2207,7 @@
|
||||
FA08F5B516C7530100F007B5 /* Source.cpp in Sources */,
|
||||
FAF6705118184FF800DBDEEA /* wuff_internal.c in Sources */,
|
||||
FA08F5B616C7530A00F007B5 /* Audio.cpp in Sources */,
|
||||
FA6F55761A6E261800062204 /* KTXHandler.cpp in Sources */,
|
||||
FA08F5B716C7530A00F007B5 /* Pool.cpp in Sources */,
|
||||
FA08F5B816C7530A00F007B5 /* Source.cpp in Sources */,
|
||||
FA08F5B916C7532A00F007B5 /* b64.cpp in Sources */,
|
||||
@@ -2198,6 +2224,7 @@
|
||||
FA08F5C216C7532A00F007B5 /* runtime.cpp in Sources */,
|
||||
FA08F5C316C7532A00F007B5 /* utf8.cpp in Sources */,
|
||||
FA0F0D5A19A2CFEB0010A75B /* glad.cpp in Sources */,
|
||||
FA6F557A1A6E261800062204 /* PVRHandler.cpp in Sources */,
|
||||
FA08F5C416C7532A00F007B5 /* Variant.cpp in Sources */,
|
||||
FA08F5C516C7532A00F007B5 /* Vector.cpp in Sources */,
|
||||
FA08F5C616C7532A00F007B5 /* wrap_Data.cpp in Sources */,
|
||||
@@ -2340,6 +2367,7 @@
|
||||
FA08F64F16C7546400F007B5 /* RevoluteJoint.cpp in Sources */,
|
||||
FA08F65016C7546400F007B5 /* RopeJoint.cpp in Sources */,
|
||||
FA08F65116C7546400F007B5 /* Shape.cpp in Sources */,
|
||||
FA6F55781A6E261800062204 /* PKMHandler.cpp in Sources */,
|
||||
FA08F65216C7547300F007B5 /* WeldJoint.cpp in Sources */,
|
||||
FAF6704C18184FF800DBDEEA /* wuff.c in Sources */,
|
||||
FA08F65316C7547300F007B5 /* WheelJoint.cpp in Sources */,
|
||||
|
||||
@@ -47,6 +47,27 @@ typedef uint32_t uint32;
|
||||
typedef int64_t int64;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
static inline uint16 swap16(uint16 x)
|
||||
{
|
||||
return (x >> 8) | (x << 8);
|
||||
}
|
||||
|
||||
static inline uint32 swap32(uint32 x)
|
||||
{
|
||||
return ((x & 0x000000FF) << 24) |
|
||||
((x & 0x0000FF00) << 8) |
|
||||
((x & 0x00FF0000) >> 8) |
|
||||
((x & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
static inline uint64 swap64(uint64 x)
|
||||
{
|
||||
return ((x << 56) & 0xFF00000000000000ULL) | ((x << 40) & 0x00FF000000000000ULL) |
|
||||
((x << 24) & 0x0000FF0000000000ULL) | ((x << 8) & 0x000000FF00000000ULL) |
|
||||
((x >> 8) & 0x00000000FF000000ULL) | ((x >> 24) & 0x0000000000FF0000ULL) |
|
||||
((x >> 40) & 0x000000000000FF00ULL) | ((x >> 56) & 0x00000000000000FFULL);
|
||||
}
|
||||
|
||||
} // love
|
||||
|
||||
#endif // LOVE_INT_H
|
||||
|
||||
@@ -507,6 +507,20 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const
|
||||
return GL_COMPRESSED_RGBA_BPTC_UNORM;
|
||||
case image::CompressedData::FORMAT_BC7SRGB:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
|
||||
case image::CompressedData::FORMAT_ETC1:
|
||||
// The ETC2 format can load ETC1 textures.
|
||||
if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility)
|
||||
return GL_COMPRESSED_RGB8_ETC2;
|
||||
else
|
||||
return GL_ETC1_RGB8_OES;
|
||||
case image::CompressedData::FORMAT_PVR1_RGB2:
|
||||
return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
|
||||
case image::CompressedData::FORMAT_PVR1_RGB4:
|
||||
return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
|
||||
case image::CompressedData::FORMAT_PVR1_RGBA2:
|
||||
return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
|
||||
case image::CompressedData::FORMAT_PVR1_RGBA4:
|
||||
return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
|
||||
default:
|
||||
if (flags.sRGB)
|
||||
return GL_SRGB8_ALPHA8;
|
||||
@@ -540,6 +554,14 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
|
||||
case image::CompressedData::FORMAT_BC7:
|
||||
case image::CompressedData::FORMAT_BC7SRGB:
|
||||
return GLAD_VERSION_4_2 || GLAD_ARB_texture_compression_bptc;
|
||||
case image::CompressedData::FORMAT_ETC1:
|
||||
// ETC2 support guarantees ETC1 support as well.
|
||||
return GLAD_ES_VERSION_3_0 || GLAD_VERSION_4_3 || GLAD_ARB_ES3_compatibility || GLAD_OES_compressed_ETC1_RGB8_texture;
|
||||
case image::CompressedData::FORMAT_PVR1_RGB2:
|
||||
case image::CompressedData::FORMAT_PVR1_RGB4:
|
||||
case image::CompressedData::FORMAT_PVR1_RGBA2:
|
||||
case image::CompressedData::FORMAT_PVR1_RGBA4:
|
||||
return GLAD_IMG_texture_compression_pvrtc;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace image
|
||||
|
||||
CompressedData::CompressedData()
|
||||
: format(FORMAT_UNKNOWN)
|
||||
, data(0)
|
||||
, data(nullptr)
|
||||
, dataSize(0)
|
||||
{
|
||||
}
|
||||
@@ -114,6 +114,11 @@ StringMap<CompressedData::Format, CompressedData::FORMAT_MAX_ENUM>::Entry Compre
|
||||
{"bc6hs", CompressedData::FORMAT_BC6Hs},
|
||||
{"bc7", CompressedData::FORMAT_BC7},
|
||||
{"bc7srgb", CompressedData::FORMAT_BC7SRGB},
|
||||
{"etc1", CompressedData::FORMAT_ETC1},
|
||||
{"pvr1rgb2", CompressedData::FORMAT_PVR1_RGB2},
|
||||
{"pvr1rgb4", CompressedData::FORMAT_PVR1_RGB4},
|
||||
{"pvr1rgba2", CompressedData::FORMAT_PVR1_RGBA2},
|
||||
{"pvr1rgba4", CompressedData::FORMAT_PVR1_RGBA4},
|
||||
};
|
||||
|
||||
StringMap<CompressedData::Format, CompressedData::FORMAT_MAX_ENUM> CompressedData::formats(CompressedData::formatEntries, sizeof(CompressedData::formatEntries));
|
||||
|
||||
@@ -58,6 +58,11 @@ public:
|
||||
FORMAT_BC6Hs,
|
||||
FORMAT_BC7,
|
||||
FORMAT_BC7SRGB,
|
||||
FORMAT_ETC1,
|
||||
FORMAT_PVR1_RGB2,
|
||||
FORMAT_PVR1_RGB4,
|
||||
FORMAT_PVR1_RGBA2,
|
||||
FORMAT_PVR1_RGBA4,
|
||||
FORMAT_MAX_ENUM
|
||||
};
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
#include "CompressedData.h"
|
||||
|
||||
#include "ddsHandler.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
@@ -29,9 +27,39 @@ namespace image
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
CompressedData::CompressedData(love::filesystem::FileData *filedata)
|
||||
CompressedData::CompressedData(std::list<CompressedFormatHandler *> formats, love::filesystem::FileData *filedata)
|
||||
{
|
||||
load(filedata);
|
||||
CompressedFormatHandler *parser = nullptr;
|
||||
|
||||
for (CompressedFormatHandler *handler : formats)
|
||||
{
|
||||
if (handler->canParse(filedata))
|
||||
{
|
||||
parser = handler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parser == nullptr)
|
||||
throw love::Exception("Could not parse compressed data: Unknown format.");
|
||||
|
||||
// DataImages SubImage vector will be populated by a parser.
|
||||
data = parser->parse(filedata, dataImages, dataSize, format);
|
||||
|
||||
if (data == nullptr)
|
||||
throw love::Exception("Could not parse compressed data.");
|
||||
|
||||
if (format == FORMAT_UNKNOWN)
|
||||
{
|
||||
delete[] data;
|
||||
throw love::Exception("Could not parse compressed data: Unknown format.");
|
||||
}
|
||||
|
||||
if (dataImages.size() == 0 || dataSize == 0)
|
||||
{
|
||||
delete[] data;
|
||||
throw love::Exception("Could not parse compressed data: No valid data?");
|
||||
}
|
||||
}
|
||||
|
||||
CompressedData::~CompressedData()
|
||||
@@ -39,51 +67,6 @@ CompressedData::~CompressedData()
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void CompressedData::load(love::filesystem::FileData *filedata)
|
||||
{
|
||||
// SubImage vector will be populated by a parser.
|
||||
std::vector<SubImage> parsedimages;
|
||||
Format texformat = FORMAT_UNKNOWN;
|
||||
|
||||
uint8 *newdata = 0;
|
||||
size_t newdata_size = 0;
|
||||
|
||||
if (ddsHandler::canParse(filedata))
|
||||
newdata = ddsHandler::parse(filedata, parsedimages, newdata_size, texformat);
|
||||
|
||||
if (newdata == 0)
|
||||
throw love::Exception("Could not parse compressed data.");
|
||||
|
||||
if (texformat == FORMAT_UNKNOWN)
|
||||
{
|
||||
delete[] newdata;
|
||||
throw love::Exception("Could not parse compressed data: Unknown format.");
|
||||
}
|
||||
|
||||
if (parsedimages.size() == 0 || newdata_size == 0)
|
||||
{
|
||||
delete[] newdata;
|
||||
throw love::Exception("Could not parse compressed data: No valid data?");
|
||||
}
|
||||
|
||||
// Make sure to clean up any previously loaded data.
|
||||
delete[] data;
|
||||
|
||||
data = newdata;
|
||||
dataSize = newdata_size;
|
||||
|
||||
dataImages = parsedimages;
|
||||
format = texformat;
|
||||
}
|
||||
|
||||
bool CompressedData::isCompressed(love::filesystem::FileData *filedata)
|
||||
{
|
||||
if (ddsHandler::canParse(filedata))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
|
||||
@@ -22,9 +22,13 @@
|
||||
#define LOVE_IMAGE_MAGPIE_COMPRESSED_DATA_H
|
||||
|
||||
// LOVE
|
||||
#include "CompressedFormatHandler.h"
|
||||
#include "filesystem/FileData.h"
|
||||
#include "image/CompressedData.h"
|
||||
|
||||
// C++
|
||||
#include <list>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
@@ -36,15 +40,9 @@ class CompressedData : public love::image::CompressedData
|
||||
{
|
||||
public:
|
||||
|
||||
CompressedData(love::filesystem::FileData *filedata);
|
||||
CompressedData(std::list<CompressedFormatHandler *> formats, love::filesystem::FileData *filedata);
|
||||
virtual ~CompressedData();
|
||||
|
||||
static bool isCompressed(love::filesystem::FileData *filedata);
|
||||
|
||||
private:
|
||||
|
||||
void load(love::filesystem::FileData *filedata);
|
||||
|
||||
}; // CompressedData
|
||||
|
||||
} // magpie
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2014 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
|
||||
#define LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
|
||||
|
||||
// LOVE
|
||||
#include "filesystem/FileData.h"
|
||||
#include "image/CompressedData.h"
|
||||
#include "common/Object.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
/**
|
||||
* Base class for all CompressedData parser library interfaces.
|
||||
* We inherit from love::Object to take advantage of reference counting...
|
||||
**/
|
||||
class CompressedFormatHandler : public love::Object
|
||||
{
|
||||
public:
|
||||
|
||||
CompressedFormatHandler() {}
|
||||
virtual ~CompressedFormatHandler() {}
|
||||
|
||||
/**
|
||||
* Determines whether a particular FileData can be parsed as CompressedData
|
||||
* by this handler.
|
||||
* @param data The data to parse.
|
||||
**/
|
||||
virtual bool canParse(const filesystem::FileData *data) = 0;
|
||||
|
||||
/**
|
||||
* Parses compressed image filedata into a list of sub-images and returns
|
||||
* a single block of memory containing all the images.
|
||||
*
|
||||
* @param[in] filedata The data to parse.
|
||||
* @param[out] image The list of sub-images generated. Byte data is a pointer
|
||||
* to the returned data.
|
||||
* @param[out] dataSize The total size in bytes of the returned data.
|
||||
* @param[out] format The format of the Compressed Data.
|
||||
*
|
||||
* @return The single block of memory containing the parsed images.
|
||||
**/
|
||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format) = 0;
|
||||
|
||||
}; // CompressedFormatHandler
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
|
||||
#endif // LOVE_IMAGE_MAGPIE_COMPRESSED_HANDLER_H
|
||||
@@ -1,228 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "DevilHandler.h"
|
||||
|
||||
// LOVE
|
||||
#include "common/Exception.h"
|
||||
#include "common/math.h"
|
||||
|
||||
// DevIL
|
||||
#include <IL/il.h>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
static inline void ilxClearErrors()
|
||||
{
|
||||
while (ilGetError() != IL_NO_ERROR);
|
||||
}
|
||||
|
||||
DevilHandler::DevilHandler()
|
||||
: mutex(nullptr)
|
||||
{
|
||||
// There should only ever be one DevilHandler object (owned by the Image
|
||||
// module), so we can use the global initialization function here.
|
||||
ilInit();
|
||||
ilEnable(IL_ORIGIN_SET);
|
||||
ilOriginFunc(IL_ORIGIN_UPPER_LEFT);
|
||||
}
|
||||
|
||||
DevilHandler::~DevilHandler()
|
||||
{
|
||||
ilShutDown();
|
||||
|
||||
if (mutex)
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
bool DevilHandler::canDecode(love::filesystem::FileData * /*data*/)
|
||||
{
|
||||
// DevIL can decode a lot of formats...
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DevilHandler::canEncode(ImageData::Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case ImageData::FORMAT_BMP:
|
||||
case ImageData::FORMAT_TGA:
|
||||
case ImageData::FORMAT_JPG:
|
||||
case ImageData::FORMAT_PNG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DevilHandler::DecodedImage DevilHandler::decode(love::filesystem::FileData *data)
|
||||
{
|
||||
if (!mutex)
|
||||
mutex = love::thread::newMutex();
|
||||
|
||||
love::thread::Lock lock(mutex);
|
||||
|
||||
ILuint image = ilGenImage();
|
||||
ilBindImage(image);
|
||||
|
||||
DecodedImage img;
|
||||
|
||||
try
|
||||
{
|
||||
bool success = ilLoadL(IL_TYPE_UNKNOWN, (void *)data->getData(), (ILuint) data->getSize()) == IL_TRUE;
|
||||
|
||||
if (!success)
|
||||
throw love::Exception("Could not decode image!");
|
||||
|
||||
img.width = ilGetInteger(IL_IMAGE_WIDTH);
|
||||
img.height = ilGetInteger(IL_IMAGE_HEIGHT);
|
||||
|
||||
// Make sure the image is in RGBA format.
|
||||
ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
|
||||
|
||||
// This should always be four.
|
||||
int bpp = ilGetInteger(IL_IMAGE_BPP);
|
||||
if (bpp != sizeof(pixel))
|
||||
throw love::Exception("Could not convert image!");
|
||||
|
||||
img.size = (size_t) ilGetInteger(IL_IMAGE_SIZE_OF_DATA);
|
||||
|
||||
try
|
||||
{
|
||||
img.data = new ILubyte[img.size];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
memcpy(img.data, ilGetData(), img.size);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
// catches love and std exceptions
|
||||
ilDeleteImage(image);
|
||||
throw love::Exception("%s", e.what());
|
||||
}
|
||||
|
||||
ilDeleteImage(image);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
DevilHandler::EncodedImage DevilHandler::encode(const DecodedImage &img, ImageData::Format format)
|
||||
{
|
||||
if (!mutex)
|
||||
mutex = love::thread::newMutex();
|
||||
|
||||
love::thread::Lock lock(mutex);
|
||||
|
||||
ILuint tempimage = ilGenImage();
|
||||
ilBindImage(tempimage);
|
||||
ilxClearErrors();
|
||||
|
||||
EncodedImage encodedimage;
|
||||
|
||||
try
|
||||
{
|
||||
bool success = ilTexImage(img.width, img.height, 1, sizeof(pixel), IL_RGBA, IL_UNSIGNED_BYTE, img.data) == IL_TRUE;
|
||||
|
||||
ILenum err = ilGetError();
|
||||
ilxClearErrors();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
if (err != IL_NO_ERROR)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case IL_ILLEGAL_OPERATION:
|
||||
throw love::Exception("Illegal operation");
|
||||
case IL_INVALID_PARAM:
|
||||
throw love::Exception("Invalid parameters");
|
||||
case IL_OUT_OF_MEMORY:
|
||||
throw love::Exception("Out of memory");
|
||||
default:
|
||||
throw love::Exception("Unknown error (%d)", (int) err);
|
||||
}
|
||||
}
|
||||
|
||||
throw love::Exception("Could not create image for the encoding!");
|
||||
}
|
||||
|
||||
ilRegisterOrigin(IL_ORIGIN_UPPER_LEFT);
|
||||
|
||||
ILuint ilFormat;
|
||||
switch (format)
|
||||
{
|
||||
case ImageData::FORMAT_BMP:
|
||||
ilFormat = IL_BMP;
|
||||
break;
|
||||
case ImageData::FORMAT_TGA:
|
||||
ilFormat = IL_TGA;
|
||||
break;
|
||||
case ImageData::FORMAT_JPG:
|
||||
ilFormat = IL_JPG;
|
||||
break;
|
||||
case ImageData::FORMAT_PNG:
|
||||
default: // PNG is the default format
|
||||
ilFormat = IL_PNG;
|
||||
break;
|
||||
}
|
||||
|
||||
encodedimage.size = ilSaveL(ilFormat, NULL, 0);
|
||||
if (!encodedimage.size)
|
||||
throw love::Exception("Could not encode image!");
|
||||
|
||||
try
|
||||
{
|
||||
encodedimage.data = new ILubyte[encodedimage.size];
|
||||
}
|
||||
catch(std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory");
|
||||
}
|
||||
|
||||
ilSaveL(ilFormat, encodedimage.data, encodedimage.size);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
// Catches love and std exceptions.
|
||||
ilDeleteImage(tempimage);
|
||||
delete[] encodedimage.data;
|
||||
encodedimage.data = 0;
|
||||
throw love::Exception("%s", e.what());
|
||||
}
|
||||
|
||||
ilDeleteImage(tempimage);
|
||||
|
||||
return encodedimage;
|
||||
}
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
@@ -27,6 +27,11 @@
|
||||
#include "PNGHandler.h"
|
||||
#include "STBHandler.h"
|
||||
|
||||
#include "ddsHandler.h"
|
||||
#include "PVRHandler.h"
|
||||
#include "KTXHandler.h"
|
||||
#include "PKMHandler.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
@@ -39,6 +44,11 @@ Image::Image()
|
||||
formatHandlers.push_back(new PNGHandler);
|
||||
formatHandlers.push_back(new JPEGHandler);
|
||||
formatHandlers.push_back(new STBHandler);
|
||||
|
||||
compressedFormatHandlers.push_back(new DDSHandler);
|
||||
compressedFormatHandlers.push_back(new PVRHandler);
|
||||
compressedFormatHandlers.push_back(new KTXHandler);
|
||||
compressedFormatHandlers.push_back(new PKMHandler);
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
@@ -47,6 +57,9 @@ Image::~Image()
|
||||
// release them instead of deleting them completely here.
|
||||
for (FormatHandler *handler : formatHandlers)
|
||||
handler->release();
|
||||
|
||||
for (CompressedFormatHandler *handler : compressedFormatHandlers)
|
||||
handler->release();
|
||||
}
|
||||
|
||||
const char *Image::getName() const
|
||||
@@ -71,12 +84,18 @@ love::image::ImageData *Image::newImageData(int width, int height, void *data, b
|
||||
|
||||
love::image::CompressedData *Image::newCompressedData(love::filesystem::FileData *data)
|
||||
{
|
||||
return new CompressedData(data);
|
||||
return new CompressedData(compressedFormatHandlers, data);
|
||||
}
|
||||
|
||||
bool Image::isCompressed(love::filesystem::FileData *data)
|
||||
{
|
||||
return CompressedData::isCompressed(data);
|
||||
for (CompressedFormatHandler *handler : compressedFormatHandlers)
|
||||
{
|
||||
if (handler->canParse(data))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // magpie
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
// LOVE
|
||||
#include "image/Image.h"
|
||||
#include "FormatHandler.h"
|
||||
#include "CompressedFormatHandler.h"
|
||||
|
||||
// C++
|
||||
#include <list>
|
||||
@@ -63,6 +64,9 @@ private:
|
||||
// Image format handlers we can use for decoding and encoding ImageData.
|
||||
std::list<FormatHandler *> formatHandlers;
|
||||
|
||||
// Compressed image format handers we can use for parsing CompressedData.
|
||||
std::list<CompressedFormatHandler *> compressedFormatHandlers;
|
||||
|
||||
}; // Image
|
||||
|
||||
} // magpie
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2014 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "KTXHandler.h"
|
||||
#include "common/int.h"
|
||||
|
||||
// C
|
||||
#include <string.h>
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
#define KTX_IDENTIFIER_REF {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}
|
||||
#define KTX_ENDIAN_REF (0x04030201)
|
||||
#define KTX_ENDIAN_REF_REV (0x01020304)
|
||||
#define KTX_HEADER_SIZE (64)
|
||||
|
||||
struct KTXHeader
|
||||
{
|
||||
uint8 identifier[12];
|
||||
uint32 endianness;
|
||||
uint32 glType;
|
||||
uint32 glTypeSize;
|
||||
uint32 glFormat;
|
||||
uint32 glInternalFormat;
|
||||
uint32 glBaseInternalFormat;
|
||||
uint32 pixelWidth;
|
||||
uint32 pixelHeight;
|
||||
uint32 pixelDepth;
|
||||
uint32 numberOfArrayElements;
|
||||
uint32 numberOfFaces;
|
||||
uint32 numberOfMipmapLevels;
|
||||
uint32 bytesOfKeyValueData;
|
||||
};
|
||||
|
||||
static_assert(sizeof(KTXHeader) == KTX_HEADER_SIZE, "Real size of KTX header doesn't match struct size!");
|
||||
|
||||
enum KTXGLInternalFormat
|
||||
{
|
||||
KTX_GL_ETC1_RGB8_OES = 0x8D64,
|
||||
|
||||
// LOVE doesn't support EAC or ETC2 yet, but it won't be hard to add.
|
||||
KTX_GL_COMPRESSED_R11_EAC = 0x9270,
|
||||
KTX_GL_COMPRESSED_SIGNED_R11_EAC = 0x9271,
|
||||
KTX_GL_COMPRESSED_RG11_EAC = 0x9272,
|
||||
KTX_GL_COMPRESSED_SIGNED_RG11_EAC = 0x9273,
|
||||
KTX_GL_COMPRESSED_RGB8_ETC2 = 0x9274,
|
||||
KTX_GL_COMPRESSED_SRGB8_ETC2 = 0x9275,
|
||||
KTX_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276,
|
||||
KTX_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277,
|
||||
KTX_GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278,
|
||||
KTX_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279,
|
||||
|
||||
// I don't know if any KTX file contains PVR data, but why not support it.
|
||||
KTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00,
|
||||
KTX_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01,
|
||||
KTX_GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02,
|
||||
KTX_GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03,
|
||||
|
||||
// Same with DXT1/3/5.
|
||||
KTX_GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0,
|
||||
KTX_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2,
|
||||
KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3
|
||||
};
|
||||
|
||||
CompressedData::Format convertFormat(uint32 glformat)
|
||||
{
|
||||
switch (glformat)
|
||||
{
|
||||
case KTX_GL_ETC1_RGB8_OES:
|
||||
return CompressedData::FORMAT_ETC1;
|
||||
case KTX_GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
|
||||
return CompressedData::FORMAT_PVR1_RGB4;
|
||||
case KTX_GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
|
||||
return CompressedData::FORMAT_PVR1_RGB2;
|
||||
case KTX_GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
|
||||
return CompressedData::FORMAT_PVR1_RGBA4;
|
||||
case KTX_GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
|
||||
return CompressedData::FORMAT_PVR1_RGBA2;
|
||||
case KTX_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
|
||||
return CompressedData::FORMAT_DXT1;
|
||||
case KTX_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
||||
return CompressedData::FORMAT_DXT3;
|
||||
case KTX_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
return CompressedData::FORMAT_DXT5;
|
||||
default:
|
||||
return CompressedData::FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace.
|
||||
|
||||
bool KTXHandler::canParse(const filesystem::FileData *data)
|
||||
{
|
||||
if (data->getSize() < sizeof(KTXHeader))
|
||||
return false;
|
||||
|
||||
KTXHeader *header = (KTXHeader *) data->getData();
|
||||
uint8 ktxidentifier[12] = KTX_IDENTIFIER_REF;
|
||||
|
||||
if (memcmp(header->identifier, ktxidentifier, 12) != 0)
|
||||
return false;
|
||||
|
||||
if (header->endianness != KTX_ENDIAN_REF && header->endianness != KTX_ENDIAN_REF_REV)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 *KTXHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||
{
|
||||
if (!canParse(filedata))
|
||||
throw love::Exception("Could not decode compressed data (not a KTX file?)");
|
||||
|
||||
KTXHeader header = *(KTXHeader *) filedata->getData();
|
||||
|
||||
if (header.endianness == KTX_ENDIAN_REF_REV)
|
||||
{
|
||||
uint32 *headerArray = (uint32 *) &header.glType;
|
||||
for (int i = 0; i < 12; i++)
|
||||
headerArray[i] = swap32(headerArray[i]);
|
||||
}
|
||||
|
||||
header.numberOfMipmapLevels = std::max(header.numberOfMipmapLevels, 1u);
|
||||
|
||||
CompressedData::Format cformat = convertFormat(header.glInternalFormat);
|
||||
|
||||
if (cformat == CompressedData::FORMAT_UNKNOWN)
|
||||
throw love::Exception("Unsupported image format in KTX file.");
|
||||
|
||||
if (header.numberOfArrayElements > 0)
|
||||
throw love::Exception("Texture arrays in KTX files are not supported.");
|
||||
|
||||
if (header.pixelDepth > 1)
|
||||
throw love::Exception("3D textures in KTX files are not supported.");
|
||||
|
||||
if (header.numberOfFaces > 1)
|
||||
throw love::Exception("Cubemap textures in KTX files are not supported.");
|
||||
|
||||
size_t fileoffset = sizeof(KTXHeader) + header.bytesOfKeyValueData;
|
||||
const uint8 *filebytes = (uint8 *) filedata->getData();
|
||||
size_t totalsize = 0;
|
||||
|
||||
// Calculate the total size needed to hold the data in memory.
|
||||
for (int i = 0; i < (int) header.numberOfMipmapLevels; i++)
|
||||
{
|
||||
if (fileoffset + sizeof(uint32) > filedata->getSize())
|
||||
throw love::Exception("Could not parse KTX file: unexpected EOF.");
|
||||
|
||||
uint32 mipsize = *(uint32 *) (filebytes + fileoffset);
|
||||
|
||||
if (header.endianness == KTX_ENDIAN_REF_REV)
|
||||
mipsize = swap32(mipsize);
|
||||
|
||||
fileoffset += sizeof(uint32);
|
||||
|
||||
// All mipsize fields are at a file offset that's a multiple of 4, so
|
||||
// there might be some padding after the actual data in this mip level.
|
||||
uint32 mipsizepadded = (mipsize + 3) & ~uint32(3);
|
||||
|
||||
totalsize += mipsizepadded;
|
||||
fileoffset += mipsizepadded;
|
||||
}
|
||||
|
||||
uint8 *data = nullptr;
|
||||
try
|
||||
{
|
||||
data = new uint8[totalsize];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
// Reset the file offset to the start of the file's image data.
|
||||
fileoffset = sizeof(KTXHeader) + header.bytesOfKeyValueData;
|
||||
size_t dataoffset = 0;
|
||||
|
||||
// Copy each mipmap level of the image from the file to our block of memory.
|
||||
for (int i = 0; i < (int) header.numberOfMipmapLevels; i++)
|
||||
{
|
||||
uint32 mipsize = *(uint32 *) (filebytes + fileoffset);
|
||||
|
||||
if (header.endianness == KTX_ENDIAN_REF_REV)
|
||||
mipsize = swap32(mipsize);
|
||||
|
||||
fileoffset += sizeof(uint32);
|
||||
|
||||
uint32 mipsizepadded = (mipsize + 3) & ~uint32(3);
|
||||
|
||||
CompressedData::SubImage mip;
|
||||
mip.width = (int) std::max(header.pixelWidth >> i, 1u);
|
||||
mip.height = (int) std::max(header.pixelHeight >> i, 1u);
|
||||
mip.size = mipsize;
|
||||
|
||||
memcpy(data + dataoffset, filebytes + fileoffset, mipsize);
|
||||
mip.data = data + dataoffset;
|
||||
|
||||
fileoffset += mipsizepadded;
|
||||
dataoffset += mipsizepadded;
|
||||
|
||||
images.push_back(mip);
|
||||
}
|
||||
|
||||
dataSize = totalsize;
|
||||
format = cformat;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2015 LOVE Development Team
|
||||
* Copyright (c) 2006-2014 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
@@ -18,13 +18,11 @@
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_IMAGE_MAGPIE_DEVIL_HANDLER_H
|
||||
#define LOVE_IMAGE_MAGPIE_DEVIL_HANDLER_H
|
||||
#ifndef LOVE_IMAGE_MAGPIE_KTX_HANDLER_H
|
||||
#define LOVE_IMAGE_MAGPIE_KTX_HANDLER_H
|
||||
|
||||
// LOVE
|
||||
#include "filesystem/FileData.h"
|
||||
#include "FormatHandler.h"
|
||||
#include "thread/threads.h"
|
||||
#include "common/config.h"
|
||||
#include "CompressedFormatHandler.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -34,31 +32,22 @@ namespace magpie
|
||||
{
|
||||
|
||||
/**
|
||||
* Interface between ImageData and DevIL.
|
||||
* Handles KTX files with compressed image data inside.
|
||||
**/
|
||||
class DevilHandler : public FormatHandler
|
||||
class KTXHandler : public CompressedFormatHandler
|
||||
{
|
||||
public:
|
||||
|
||||
// Implements FormatHandler.
|
||||
virtual ~KTXHandler() {}
|
||||
|
||||
DevilHandler();
|
||||
virtual ~DevilHandler();
|
||||
// Implements CompressedFormatHandler.
|
||||
virtual bool canParse(const filesystem::FileData *data);
|
||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||
|
||||
virtual bool canDecode(love::filesystem::FileData *data);
|
||||
virtual bool canEncode(ImageData::Format format);
|
||||
|
||||
virtual DecodedImage decode(love::filesystem::FileData *data);
|
||||
virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
|
||||
|
||||
private:
|
||||
|
||||
Mutex *mutex;
|
||||
|
||||
}; // DevilHandler
|
||||
}; // KTXHandler
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
|
||||
#endif // LOVE_IMAGE_MAGPIE_DEVIL_HANDLER_H
|
||||
#endif // LOVE_IMAGE_MAGPIE_KTX_HANDLER_H
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2014 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "PKMHandler.h"
|
||||
#include "common/int.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Big endian to host (and vice versa.)
|
||||
inline uint16 swap16big(uint16 x)
|
||||
{
|
||||
#ifdef LOVE_BIG_ENDIAN
|
||||
return x;
|
||||
#else
|
||||
return swap16(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const uint8 pkmIdentifier[] = {'P','K','M',' '};
|
||||
|
||||
struct PKMHeader
|
||||
{
|
||||
uint8 identifier[4];
|
||||
uint8 version[2];
|
||||
uint16 textureFormatBig;
|
||||
uint16 extendedWidthBig;
|
||||
uint16 extendedHeightBig;
|
||||
uint16 widthBig;
|
||||
uint16 heightBig;
|
||||
};
|
||||
|
||||
enum PKMTextureFormat
|
||||
{
|
||||
ETC1_RGB_NO_MIPMAPS = 0,
|
||||
ETC2PACKAGE_RGB_NO_MIPMAPS,
|
||||
ETC2PACKAGE_RGBA_NO_MIPMAPS_OLD,
|
||||
ETC2PACKAGE_RGBA_NO_MIPMAPS,
|
||||
ETC2PACKAGE_RGBA1_NO_MIPMAPS,
|
||||
ETC2PACKAGE_R_NO_MIPMAPS,
|
||||
ETC2PACKAGE_RG_NO_MIPMAPS,
|
||||
ETC2PACKAGE_R_SIGNED_NO_MIPMAPS,
|
||||
ETC2PACKAGE_RG_SIGNED_NO_MIPMAPS
|
||||
};
|
||||
|
||||
CompressedData::Format convertFormat(uint16 texformat)
|
||||
{
|
||||
switch (texformat)
|
||||
{
|
||||
case ETC1_RGB_NO_MIPMAPS:
|
||||
return CompressedData::FORMAT_ETC1;
|
||||
default:
|
||||
return CompressedData::FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace.
|
||||
|
||||
bool PKMHandler::canParse(const filesystem::FileData *data)
|
||||
{
|
||||
if (data->getSize() <= sizeof(PKMHeader))
|
||||
return false;
|
||||
|
||||
const PKMHeader *header = (const PKMHeader *) data->getData();
|
||||
|
||||
if (memcmp(header->identifier, pkmIdentifier, 4) != 0)
|
||||
return false;
|
||||
|
||||
// At the time of this writing, only v1.0 and v2.0 exist.
|
||||
if ((header->version[0] != '2' && header->version[0] != '1') || header->version[1] != '0')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 *PKMHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||
{
|
||||
if (!canParse(filedata))
|
||||
throw love::Exception("Could not decode compressed data (not a PKM file?)");
|
||||
|
||||
PKMHeader header = *(const PKMHeader *) filedata->getData();
|
||||
|
||||
header.textureFormatBig = swap16big(header.textureFormatBig);
|
||||
header.extendedWidthBig = swap16big(header.extendedWidthBig);
|
||||
header.extendedHeightBig = swap16big(header.extendedHeightBig);
|
||||
header.widthBig = swap16big(header.widthBig);
|
||||
header.heightBig = swap16big(header.heightBig);
|
||||
|
||||
CompressedData::Format cformat = convertFormat(header.textureFormatBig);
|
||||
|
||||
if (cformat == CompressedData::FORMAT_UNKNOWN)
|
||||
throw love::Exception("Could not parse PKM file: unsupported texture format.");
|
||||
|
||||
// The rest of the file after the header is all texture data.
|
||||
size_t totalsize = filedata->getSize() - sizeof(PKMHeader);
|
||||
uint8 *data = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
data = new uint8[totalsize];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
// PKM files only store a single mipmap level.
|
||||
memcpy(data, (uint8 *) filedata->getData() + sizeof(PKMHeader), totalsize);
|
||||
|
||||
CompressedData::SubImage mip;
|
||||
|
||||
// TODO: verify whether glCompressedTexImage works properly with the unpadded
|
||||
// width and height values (extended == padded.)
|
||||
mip.width = header.widthBig;
|
||||
mip.height = header.heightBig;
|
||||
|
||||
mip.size = totalsize;
|
||||
mip.data = data;
|
||||
|
||||
images.push_back(mip);
|
||||
|
||||
dataSize = totalsize;
|
||||
format = cformat;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2014 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_IMAGE_MAGPIE_PKM_HANDLER_H
|
||||
#define LOVE_IMAGE_MAGPIE_PKM_HANDLER_H
|
||||
|
||||
#include "common/config.h"
|
||||
#include "CompressedFormatHandler.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
/**
|
||||
* Handles PKM files with compressed ETC data inside.
|
||||
**/
|
||||
class PKMHandler : public CompressedFormatHandler
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~PKMHandler() {}
|
||||
|
||||
// Implements CompressedFormatHandler.
|
||||
virtual bool canParse(const filesystem::FileData *data);
|
||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||
|
||||
}; // PKMHandler
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
|
||||
#endif // LOVE_IMAGE_MAGPIE_PKM_HANDLER_H
|
||||
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2014 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "PVRHandler.h"
|
||||
#include "common/int.h"
|
||||
|
||||
// C++
|
||||
#include <algorithm>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// 'P' 'V' 'R' 3
|
||||
static const uint32 PVRTEX3_IDENT = 0x03525650;
|
||||
static const uint32 PVRTEX3_IDENT_REV = 0x50565203;
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct PVRTexHeaderV3
|
||||
{
|
||||
uint32 version; /// Version of the file header, used to identify it.
|
||||
uint32 flags; /// Various format flags.
|
||||
uint64 pixelFormat; /// The pixel format, 8cc value storing the 4 channel identifiers and their respective sizes.
|
||||
uint32 colorSpace; /// The Color Space of the texture, currently either linear RGB or sRGB.
|
||||
uint32 channelType; /// Variable type that the channel is stored in. Supports signed/unsigned int/short/byte or float for now.
|
||||
uint32 height; /// Height of the texture.
|
||||
uint32 width; /// Width of the texture.
|
||||
uint32 depth; /// Depth of the texture. (Z-slices)
|
||||
uint32 numSurfaces; /// Number of members in a Texture Array.
|
||||
uint32 numFaces; /// Number of faces in a Cube Map. Maybe be a value other than 6.
|
||||
uint32 numMipmaps; /// Number of MIP Maps in the texture - NB: Includes top level.
|
||||
uint32 metaDataSize; /// Size of the accompanying meta data.
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
enum PVRV3PixelFormat
|
||||
{
|
||||
ePVRTPF_PVRTCI_2bpp_RGB = 0x00,
|
||||
ePVRTPF_PVRTCI_2bpp_RGBA,
|
||||
ePVRTPF_PVRTCI_4bpp_RGB,
|
||||
ePVRTPF_PVRTCI_4bpp_RGBA,
|
||||
ePVRTPF_PVRTCII_2bpp,
|
||||
ePVRTPF_PVRTCII_4bpp,
|
||||
ePVRTPF_ETC1 = 0x06,
|
||||
ePVRTPF_DXT1,
|
||||
ePVRTPF_DXT2,
|
||||
ePVRTPF_DXT3,
|
||||
ePVRTPF_DXT4,
|
||||
ePVRTPF_DXT5,
|
||||
ePVRTF_UNKNOWN_FORMAT = 0x7F
|
||||
};
|
||||
|
||||
// 'P' 'V' 'R' '!'
|
||||
static const uint32 PVRTEX2_IDENT = 0x21525650;
|
||||
static const uint32 PVRTEX2_IDENT_REV = 0x50565221;
|
||||
|
||||
struct PVRTexHeaderV2
|
||||
{
|
||||
uint32 headerSize;
|
||||
uint32 height;
|
||||
uint32 width;
|
||||
uint32 numMipmaps;
|
||||
uint32 flags;
|
||||
uint32 dataSize;
|
||||
uint32 bpp;
|
||||
uint32 bitmaskRed;
|
||||
uint32 bitmaskGreen;
|
||||
uint32 bitmaskBlue;
|
||||
uint32 bitmaskAlpha;
|
||||
uint32 pvrTag;
|
||||
uint32 numSurfaces;
|
||||
};
|
||||
|
||||
// The legacy V2 pixel types we support.
|
||||
enum PVRPixelTypeV2
|
||||
{
|
||||
PixelTypePVRTC2 = 0x18,
|
||||
PixelTypePVRTC4,
|
||||
PixelTypePVRTCII2 = 0x1C,
|
||||
PixelTypePVRTCII4,
|
||||
PixelTypeDXT1 = 0x20,
|
||||
PixelTypeDXT3 = 0x22,
|
||||
PixelTypeDXT5 = 0x24,
|
||||
PixelTypeETC1 = 0x36
|
||||
};
|
||||
|
||||
// Convert a V2 header to V3.
|
||||
void ConvertPVRHeader(PVRTexHeaderV2 header2, PVRTexHeaderV3 *header3)
|
||||
{
|
||||
// If the header's endianness doesn't match our own, we swap everything.
|
||||
if (header2.pvrTag == PVRTEX2_IDENT_REV)
|
||||
{
|
||||
// All of the struct's members are uint32 values, so we can do this.
|
||||
uint32 *headerArray = (uint32 *) &header2;
|
||||
for (size_t i = 0; i < sizeof(PVRTexHeaderV2) / sizeof(uint32); i++)
|
||||
headerArray[i] = swap32(headerArray[i]);
|
||||
}
|
||||
|
||||
memset(header3, 0, sizeof(PVRTexHeaderV3));
|
||||
|
||||
header3->version = PVRTEX3_IDENT;
|
||||
header3->height = header2.height;
|
||||
header3->width = header2.width;
|
||||
header3->depth = 1;
|
||||
header3->numSurfaces = header2.numSurfaces;
|
||||
header3->numFaces = 1;
|
||||
header3->numMipmaps = header2.numMipmaps;
|
||||
header3->metaDataSize = 0;
|
||||
|
||||
switch ((PVRPixelTypeV2) (header2.flags & 0xFF))
|
||||
{
|
||||
case PixelTypePVRTC2:
|
||||
header3->pixelFormat = ePVRTPF_PVRTCI_2bpp_RGBA;
|
||||
break;
|
||||
case PixelTypePVRTC4:
|
||||
header3->pixelFormat = ePVRTPF_PVRTCI_4bpp_RGBA;
|
||||
break;
|
||||
case PixelTypePVRTCII2:
|
||||
header3->pixelFormat = ePVRTPF_PVRTCII_2bpp;
|
||||
break;
|
||||
case PixelTypePVRTCII4:
|
||||
header3->pixelFormat = ePVRTPF_PVRTCII_4bpp;
|
||||
break;
|
||||
case PixelTypeDXT1:
|
||||
header3->pixelFormat = ePVRTPF_DXT1;
|
||||
break;
|
||||
case PixelTypeDXT3:
|
||||
header3->pixelFormat = ePVRTPF_DXT3;
|
||||
break;
|
||||
case PixelTypeDXT5:
|
||||
header3->pixelFormat = ePVRTPF_DXT5;
|
||||
break;
|
||||
case PixelTypeETC1:
|
||||
header3->pixelFormat = ePVRTPF_ETC1;
|
||||
break;
|
||||
default:
|
||||
header3->pixelFormat = ePVRTF_UNKNOWN_FORMAT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CompressedData::Format convertFormat(PVRV3PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case ePVRTPF_PVRTCI_2bpp_RGB:
|
||||
return CompressedData::FORMAT_PVR1_RGB2;
|
||||
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
||||
return CompressedData::FORMAT_PVR1_RGBA2;
|
||||
case ePVRTPF_PVRTCI_4bpp_RGB:
|
||||
return CompressedData::FORMAT_PVR1_RGB4;
|
||||
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
||||
return CompressedData::FORMAT_PVR1_RGBA4;
|
||||
case ePVRTPF_ETC1:
|
||||
return CompressedData::FORMAT_ETC1;
|
||||
case ePVRTPF_DXT1:
|
||||
return CompressedData::FORMAT_DXT1;
|
||||
case ePVRTPF_DXT3:
|
||||
return CompressedData::FORMAT_DXT3;
|
||||
case ePVRTPF_DXT5:
|
||||
return CompressedData::FORMAT_DXT5;
|
||||
default:
|
||||
return CompressedData::FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
int getBitsPerPixel(uint64 pixelformat)
|
||||
{
|
||||
// Uncompressed formats have their bits per pixel stored in the high bits.
|
||||
if ((pixelformat & 0xFFFFFFFF) != pixelformat)
|
||||
{
|
||||
const uint8 *charformat = (const uint8 *) &pixelformat;
|
||||
return charformat[4] + charformat[5] + charformat[6] + charformat[7];
|
||||
}
|
||||
|
||||
switch (pixelformat)
|
||||
{
|
||||
case ePVRTPF_PVRTCI_2bpp_RGB:
|
||||
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
||||
case ePVRTPF_PVRTCII_2bpp:
|
||||
return 2;
|
||||
case ePVRTPF_PVRTCI_4bpp_RGB:
|
||||
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
||||
case ePVRTPF_PVRTCII_4bpp:
|
||||
case ePVRTPF_ETC1:
|
||||
case ePVRTPF_DXT1:
|
||||
return 4;
|
||||
case ePVRTPF_DXT2:
|
||||
case ePVRTPF_DXT3:
|
||||
case ePVRTPF_DXT4:
|
||||
case ePVRTPF_DXT5:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void getFormatMinDimensions(uint64 pixelformat, int &minX, int &minY)
|
||||
{
|
||||
switch (pixelformat)
|
||||
{
|
||||
case ePVRTPF_PVRTCI_2bpp_RGB:
|
||||
case ePVRTPF_PVRTCI_2bpp_RGBA:
|
||||
minX = 16;
|
||||
minY = 8;
|
||||
break;
|
||||
case ePVRTPF_PVRTCI_4bpp_RGB:
|
||||
case ePVRTPF_PVRTCI_4bpp_RGBA:
|
||||
minX = minY = 8;
|
||||
break;
|
||||
case ePVRTPF_PVRTCII_2bpp:
|
||||
minX = 8;
|
||||
minY = 4;
|
||||
break;
|
||||
case ePVRTPF_PVRTCII_4bpp:
|
||||
minX = minY = 4;
|
||||
break;
|
||||
case ePVRTPF_DXT1:
|
||||
case ePVRTPF_DXT2:
|
||||
case ePVRTPF_DXT3:
|
||||
case ePVRTPF_DXT4:
|
||||
case ePVRTPF_DXT5:
|
||||
case ePVRTPF_ETC1:
|
||||
minX = minY = 4;
|
||||
break;
|
||||
default: // We don't handle all possible formats, but that's fine.
|
||||
minX = minY = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t getMipLevelSize(const PVRTexHeaderV3 &header, int miplevel)
|
||||
{
|
||||
int smallestwidth = 1;
|
||||
int smallestheight = 1;
|
||||
getFormatMinDimensions(header.pixelFormat, smallestwidth, smallestheight);
|
||||
|
||||
int width = std::max((int) header.width >> miplevel, 1);
|
||||
int height = std::max((int) header.height >> miplevel, 1);
|
||||
int depth = std::max((int) header.depth >> miplevel, 1);
|
||||
|
||||
// Pad the dimensions.
|
||||
width += (-width) % smallestwidth;
|
||||
height += (-height) % smallestheight;
|
||||
|
||||
return getBitsPerPixel(header.pixelFormat) * width * height * depth / 8;
|
||||
}
|
||||
|
||||
} // Anonymous namespace.
|
||||
|
||||
|
||||
bool PVRHandler::canParse(const filesystem::FileData *data)
|
||||
{
|
||||
if (data->getSize() < sizeof(PVRTexHeaderV2) || data->getSize() < sizeof(PVRTexHeaderV3))
|
||||
return false;
|
||||
|
||||
PVRTexHeaderV3 *header3 = (PVRTexHeaderV3 *) data->getData();
|
||||
|
||||
// Magic number (FourCC identifier.)
|
||||
if (header3->version == PVRTEX3_IDENT || header3->version == PVRTEX3_IDENT_REV)
|
||||
return true;
|
||||
|
||||
// Maybe it has a V2 header.
|
||||
PVRTexHeaderV2 *header2 = (PVRTexHeaderV2 *) data->getData();
|
||||
|
||||
// FourCC identifier.
|
||||
if (header2->pvrTag == PVRTEX2_IDENT || header2->pvrTag == PVRTEX2_IDENT_REV)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 *PVRHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||
{
|
||||
if (!canParse(filedata))
|
||||
throw love::Exception("Could not decode compressed data (not a PVR file?)");
|
||||
|
||||
PVRTexHeaderV3 header3 = *(PVRTexHeaderV3 *) filedata->getData();
|
||||
|
||||
// If the header isn't the V3 format, assume it's V2 and convert.
|
||||
if (header3.version != PVRTEX3_IDENT && header3.version != PVRTEX3_IDENT_REV)
|
||||
ConvertPVRHeader(*(PVRTexHeaderV2 *) filedata->getData(), &header3);
|
||||
|
||||
// If the header's endianness doesn't match our own, then we swap everything.
|
||||
if (header3.version == PVRTEX3_IDENT_REV)
|
||||
{
|
||||
header3.version = PVRTEX3_IDENT;
|
||||
header3.flags = swap32(header3.flags);
|
||||
header3.pixelFormat = swap64(header3.pixelFormat);
|
||||
header3.colorSpace = swap32(header3.colorSpace);
|
||||
header3.channelType = swap32(header3.channelType);
|
||||
header3.height = swap32(header3.height);
|
||||
header3.width = swap32(header3.width);
|
||||
header3.depth = swap32(header3.depth);
|
||||
header3.numFaces = swap32(header3.numFaces);
|
||||
header3.numMipmaps = swap32(header3.numMipmaps);
|
||||
header3.metaDataSize = swap32(header3.metaDataSize);
|
||||
}
|
||||
|
||||
if (header3.depth > 1)
|
||||
throw love::Exception("Image depths greater than 1 in PVR files are unsupported.");
|
||||
|
||||
CompressedData::Format cformat = convertFormat((PVRV3PixelFormat) header3.pixelFormat);
|
||||
|
||||
if (cformat == CompressedData::FORMAT_UNKNOWN)
|
||||
throw love::Exception("Could not parse PVR file: unsupported image format.");
|
||||
|
||||
size_t totalsize = 0;
|
||||
uint8 *data = nullptr;
|
||||
|
||||
// Ignore faces and surfaces except the first ones (for now.)
|
||||
for (int i = 0; i < (int) header3.numMipmaps; i++)
|
||||
totalsize += getMipLevelSize(header3, i);
|
||||
|
||||
size_t fileoffset = sizeof(PVRTexHeaderV3) + header3.metaDataSize;
|
||||
|
||||
// Make sure the file actually holds this much data...
|
||||
if (filedata->getSize() < fileoffset + totalsize)
|
||||
throw love::Exception("Could not parse PVR file: invalid size calculation.");
|
||||
|
||||
try
|
||||
{
|
||||
data = new uint8[totalsize];
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
size_t curoffset = 0;
|
||||
const uint8 *filebytes = (uint8 *) filedata->getData() + fileoffset;
|
||||
|
||||
for (int i = 0; i < (int) header3.numMipmaps; i++)
|
||||
{
|
||||
size_t mipsize = getMipLevelSize(header3, i);
|
||||
|
||||
if (curoffset + mipsize > totalsize)
|
||||
break; // Just in case.
|
||||
|
||||
CompressedData::SubImage mip;
|
||||
mip.width = std::max((int) header3.width >> i, 1);
|
||||
mip.height = std::max((int) header3.height >> i, 1);
|
||||
mip.size = mipsize;
|
||||
|
||||
memcpy(data + curoffset, filebytes + curoffset, mipsize);
|
||||
mip.data = data + curoffset;
|
||||
|
||||
curoffset += mipsize;
|
||||
|
||||
images.push_back(mip);
|
||||
}
|
||||
|
||||
dataSize = totalsize;
|
||||
format = cformat;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2014 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_IMAGE_MAGPIE_PVR_HANDLER_H
|
||||
#define LOVE_IMAGE_MAGPIE_PVR_HANDLER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/config.h"
|
||||
#include "CompressedFormatHandler.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
class PVRHandler : public CompressedFormatHandler
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~PVRHandler() {}
|
||||
|
||||
// Implements CompressedFormatHandler.
|
||||
virtual bool canParse(const filesystem::FileData *data);
|
||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||
|
||||
}; // PVRHandler
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
|
||||
#endif // LOVE_IMAGE_MAGPIE_PVR_HANDLER_H
|
||||
@@ -27,19 +27,19 @@ namespace image
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
bool ddsHandler::canParse(const filesystem::FileData *data)
|
||||
bool DDSHandler::canParse(const filesystem::FileData *data)
|
||||
{
|
||||
return dds::isCompressedDDS(data->getData(), data->getSize());
|
||||
}
|
||||
|
||||
uint8 *ddsHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||
uint8 *DDSHandler::parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format)
|
||||
{
|
||||
if (!dds::isDDS(filedata->getData(), filedata->getSize()))
|
||||
throw love::Exception("Could not decode compressed data (not a DDS file?)");
|
||||
|
||||
CompressedData::Format texformat = CompressedData::FORMAT_UNKNOWN;
|
||||
|
||||
uint8 *data = 0;
|
||||
uint8 *data = nullptr;
|
||||
dataSize = 0;
|
||||
images.clear();
|
||||
|
||||
@@ -99,7 +99,7 @@ uint8 *ddsHandler::parse(filesystem::FileData *filedata, std::vector<CompressedD
|
||||
return data;
|
||||
}
|
||||
|
||||
CompressedData::Format ddsHandler::convertFormat(dds::Format ddsformat)
|
||||
CompressedData::Format DDSHandler::convertFormat(dds::Format ddsformat)
|
||||
{
|
||||
switch (ddsformat)
|
||||
{
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
#define LOVE_IMAGE_MAGPIE_DDS_HANDLER_H
|
||||
|
||||
// LOVE
|
||||
#include "common/EnumMap.h"
|
||||
#include "filesystem/FileData.h"
|
||||
#include "image/CompressedData.h"
|
||||
#include "CompressedFormatHandler.h"
|
||||
|
||||
// dds parser
|
||||
#include "ddsparse/ddsparse.h"
|
||||
@@ -42,36 +40,21 @@ namespace magpie
|
||||
/**
|
||||
* Interface between CompressedData and the ddsparse library.
|
||||
**/
|
||||
class ddsHandler
|
||||
class DDSHandler : public CompressedFormatHandler
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Determines whether a particular FileData can be parsed as CompressedData
|
||||
* by this handler.
|
||||
* @param data The data to parse.
|
||||
**/
|
||||
static bool canParse(const filesystem::FileData *data);
|
||||
virtual ~DDSHandler() {}
|
||||
|
||||
/**
|
||||
* Parses compressed image filedata into a list of sub-images and returns
|
||||
* a single block of memory containing all the images.
|
||||
*
|
||||
* @param[in] filedata The data to parse.
|
||||
* @param[out] images The list of sub-images generated. Byte data is a pointer
|
||||
* to the returned data.
|
||||
* @param[out] dataSize The total size in bytes of the returned data.
|
||||
* @param[out] format The format of the Compressed Data.
|
||||
*
|
||||
* @return The single block of memory containing the parsed images.
|
||||
**/
|
||||
static uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||
// Implements CompressedFormatHandler.
|
||||
virtual bool canParse(const filesystem::FileData *data);
|
||||
virtual uint8 *parse(filesystem::FileData *filedata, std::vector<CompressedData::SubImage> &images, size_t &dataSize, CompressedData::Format &format);
|
||||
|
||||
private:
|
||||
|
||||
static CompressedData::Format convertFormat(dds::Format ddsformat);
|
||||
|
||||
}; // ddsHandler
|
||||
}; // DDSHandler
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
|
||||
Reference in New Issue
Block a user