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:
Alex Szpakowski
2015-01-20 03:18:28 -04:00
parent 319ccc2f25
commit dfdd52e499
21 changed files with 1136 additions and 340 deletions
+7
View File
@@ -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
View File
@@ -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 */,
+21
View File
@@ -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
+22
View File
@@ -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;
}
+6 -1
View File
@@ -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));
+5
View File
@@ -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
};
+32 -49
View File
@@ -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
+5 -7
View File
@@ -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
-228
View File
@@ -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
+21 -2
View File
@@ -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
+4
View File
@@ -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
+241
View File
@@ -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
+156
View File
@@ -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
+53
View File
@@ -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
+385
View File
@@ -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
+51
View File
@@ -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
+4 -4
View File
@@ -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)
{
+7 -24
View File
@@ -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