diff --git a/platform/macosx/love-framework.xcodeproj/project.pbxproj b/platform/macosx/love-framework.xcodeproj/project.pbxproj index 3e532cf98..252418c19 100644 --- a/platform/macosx/love-framework.xcodeproj/project.pbxproj +++ b/platform/macosx/love-framework.xcodeproj/project.pbxproj @@ -225,7 +225,16 @@ FA577ACD16C7514C00860150 /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7716C71A0800860150 /* Vorbis.framework */; }; FA7C937A16DCC6C2006F2BEE /* wrap_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */; }; FA7C937B16DCC6C2006F2BEE /* wrap_Math.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7C937616DCC6C2006F2BEE /* wrap_Math.h */; }; + FAAC6B02170A373B008A61C5 /* CompressedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAAC6B00170A373A008A61C5 /* CompressedData.cpp */; }; + FAAC6B03170A373B008A61C5 /* CompressedData.h in Headers */ = {isa = PBXBuildFile; fileRef = FAAC6B01170A373A008A61C5 /* CompressedData.h */; }; FAAFF04416CB11C700CCDE45 /* OpenAL-Soft.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */; }; + FAE010DB170DDE99006F29D0 /* ddsinfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE010D8170DDE99006F29D0 /* ddsinfo.h */; }; + FAE010DC170DDE99006F29D0 /* ddsparse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAE010D9170DDE99006F29D0 /* ddsparse.cpp */; }; + FAE010DD170DDE99006F29D0 /* ddsparse.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE010DA170DDE99006F29D0 /* ddsparse.h */; }; + FAE010E0170DE25E006F29D0 /* CompressedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAE010DE170DE25E006F29D0 /* CompressedData.cpp */; }; + FAE010E1170DE25E006F29D0 /* CompressedData.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE010DF170DE25E006F29D0 /* CompressedData.h */; }; + FAE010E4170DF75C006F29D0 /* wrap_CompressedData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAE010E2170DF75B006F29D0 /* wrap_CompressedData.cpp */; }; + FAE010E5170DF75C006F29D0 /* wrap_CompressedData.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE010E3170DF75C006F29D0 /* wrap_CompressedData.h */; }; FAF272A416E3D44400CC193A /* Channel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF2729816E3D44400CC193A /* Channel.cpp */; }; FAF272A516E3D44400CC193A /* Channel.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF2729916E3D44400CC193A /* Channel.h */; }; FAF272A616E3D44400CC193A /* LuaThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF2729A16E3D44400CC193A /* LuaThread.cpp */; }; @@ -702,7 +711,16 @@ FA577AAF16C7507900860150 /* love.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = love.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Math.cpp; sourceTree = ""; }; FA7C937616DCC6C2006F2BEE /* wrap_Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Math.h; sourceTree = ""; }; + FAAC6B00170A373A008A61C5 /* CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompressedData.cpp; sourceTree = ""; }; + FAAC6B01170A373A008A61C5 /* CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressedData.h; sourceTree = ""; }; FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "OpenAL-Soft.framework"; path = "/Library/Frameworks/OpenAL-Soft.framework"; sourceTree = ""; }; + FAE010D8170DDE99006F29D0 /* ddsinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsinfo.h; sourceTree = ""; }; + FAE010D9170DDE99006F29D0 /* ddsparse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ddsparse.cpp; sourceTree = ""; }; + FAE010DA170DDE99006F29D0 /* ddsparse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsparse.h; sourceTree = ""; }; + FAE010DE170DE25E006F29D0 /* CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompressedData.cpp; sourceTree = ""; }; + FAE010DF170DE25E006F29D0 /* CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressedData.h; sourceTree = ""; }; + FAE010E2170DF75B006F29D0 /* wrap_CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_CompressedData.cpp; sourceTree = ""; }; + FAE010E3170DF75C006F29D0 /* wrap_CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_CompressedData.h; sourceTree = ""; }; FAF2729816E3D44400CC193A /* Channel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Channel.cpp; sourceTree = ""; }; FAF2729916E3D44400CC193A /* Channel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Channel.h; sourceTree = ""; }; FAF2729A16E3D44400CC193A /* LuaThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LuaThread.cpp; sourceTree = ""; }; @@ -748,14 +766,18 @@ 003F4BA82B6046BC133B3F0F /* image */ = { isa = PBXGroup; children = ( + FAAC6B00170A373A008A61C5 /* CompressedData.cpp */, + FAAC6B01170A373A008A61C5 /* CompressedData.h */, + 13730AB030E309FF6E2961F1 /* devil */, 25C325DC2128769F6C6A54C3 /* Image.h */, 78115E763B723C0C40AD47CF /* ImageData.cpp */, 07B301984BE42246402F7D27 /* ImageData.h */, - 13730AB030E309FF6E2961F1 /* devil */, 0B0728FA73B107B37A956A09 /* wrap_Image.cpp */, 006B015320155B4D42B43B61 /* wrap_Image.h */, 076840774B0B6E721D0C18D0 /* wrap_ImageData.cpp */, 31B85B507F466FE158A3718E /* wrap_ImageData.h */, + FAE010E2170DF75B006F29D0 /* wrap_CompressedData.cpp */, + FAE010E3170DF75C006F29D0 /* wrap_CompressedData.h */, ); path = image; sourceTree = ""; @@ -912,6 +934,8 @@ 68616BD516DB124312B47EB3 /* Image.h */, 1AA7781A230065F346E2313A /* ImageData.cpp */, 283342E174613897621A43F1 /* ImageData.h */, + FAE010DE170DE25E006F29D0 /* CompressedData.cpp */, + FAE010DF170DE25E006F29D0 /* CompressedData.h */, ); path = devil; sourceTree = ""; @@ -1194,6 +1218,7 @@ isa = PBXGroup; children = ( 63287ED84D0F2EEB65D249A3 /* Box2D */, + FAE010D7170DDE99006F29D0 /* ddsparse */, 3AED1DE005A53DDB07902760 /* luasocket */, 36C14C81334735EC54E33637 /* utf8 */, ); @@ -1619,6 +1644,16 @@ path = math; sourceTree = ""; }; + FAE010D7170DDE99006F29D0 /* ddsparse */ = { + isa = PBXGroup; + children = ( + FAE010D8170DDE99006F29D0 /* ddsinfo.h */, + FAE010D9170DDE99006F29D0 /* ddsparse.cpp */, + FAE010DA170DDE99006F29D0 /* ddsparse.h */, + ); + path = ddsparse; + sourceTree = ""; + }; FAF272B016E3D46400CC193A /* sdl */ = { isa = PBXGroup; children = ( @@ -1647,6 +1682,11 @@ FAF272B616E3D46400CC193A /* Thread.h in Headers */, FAF272B816E3D46400CC193A /* threads.h in Headers */, FA5454C316F1310000D30303 /* MathModule.h in Headers */, + FAAC6B03170A373B008A61C5 /* CompressedData.h in Headers */, + FAE010DB170DDE99006F29D0 /* ddsinfo.h in Headers */, + FAE010DD170DDE99006F29D0 /* ddsparse.h in Headers */, + FAE010E1170DE25E006F29D0 /* CompressedData.h in Headers */, + FAE010E5170DF75C006F29D0 /* wrap_CompressedData.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1922,6 +1962,10 @@ FAF272B516E3D46400CC193A /* Thread.cpp in Sources */, FAF272B716E3D46400CC193A /* threads.cpp in Sources */, FA5454C216F1310000D30303 /* MathModule.cpp in Sources */, + FAAC6B02170A373B008A61C5 /* CompressedData.cpp in Sources */, + FAE010DC170DDE99006F29D0 /* ddsparse.cpp in Sources */, + FAE010E0170DE25E006F29D0 /* CompressedData.cpp in Sources */, + FAE010E4170DF75C006F29D0 /* wrap_CompressedData.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index 84d4faae0..ae5887f48 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -469,6 +469,7 @@ StringMap::Entry typeEntries[] = // Image {"ImageData", IMAGE_IMAGE_DATA_ID}, + {"CompressedData", IMAGE_COMPRESSED_DATA_ID}, // Audio {"Source", AUDIO_SOURCE_ID}, diff --git a/src/common/types.h b/src/common/types.h index 3631c10d4..b8e151f53 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -56,6 +56,7 @@ enum Type // Image IMAGE_IMAGE_DATA_ID, + IMAGE_COMPRESSED_DATA_ID, // Audio AUDIO_SOURCE_ID, @@ -127,6 +128,7 @@ const bits GRAPHICS_SHADER_T = (bits(1) << GRAPHICS_SHADER_ID) | OBJECT_T; // Image. const bits IMAGE_IMAGE_DATA_T = (bits(1) << IMAGE_IMAGE_DATA_ID) | DATA_T; +const bits IMAGE_COMPRESSED_DATA_T = (bits(1) << IMAGE_COMPRESSED_DATA_ID) | DATA_T; // Audio. const bits AUDIO_SOURCE_T = (bits(1) << AUDIO_SOURCE_ID) | OBJECT_T; diff --git a/src/libraries/ddsparse/ddsinfo.h b/src/libraries/ddsparse/ddsinfo.h new file mode 100644 index 000000000..59760d0fe --- /dev/null +++ b/src/libraries/ddsparse/ddsinfo.h @@ -0,0 +1,235 @@ +/** + * Simple DDS data parser for compressed 2D textures. + * + * Copyright (c) 2013 Alexander Szpakowski. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * + * Enums and structs copied from Microsoft. + * http://msdn.microsoft.com/en-us/library/bb943991.aspx + **/ + +#ifndef DDS_INFO_H +#define DDS_INFO_H + +#include + +namespace dds +{ +namespace dxinfo +{ + +enum DDPF +{ + DDPF_ALPHAPIXELS = 0x000001, + DDPF_ALPHA = 0x000002, + DDPF_FOURCC = 0x000004, + DDPF_RGB = 0x000040, + DDPF_YUV = 0x000200, + DDPF_LUMINANCE = 0x020000 +}; + +enum D3D10ResourceDimension +{ + D3D10_RESOURCE_DIMENSION_UNKNOWN = 0, + D3D10_RESOURCE_DIMENSION_BUFFER = 1, + D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2, + D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3, + D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4 +}; + +enum DXGIFormat +{ + DXGI_FORMAT_UNKNOWN = 0, + + DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, + DXGI_FORMAT_R32G32B32A32_FLOAT = 2, + DXGI_FORMAT_R32G32B32A32_UINT = 3, + DXGI_FORMAT_R32G32B32A32_SINT = 4, + + DXGI_FORMAT_R32G32B32_TYPELESS = 5, + DXGI_FORMAT_R32G32B32_FLOAT = 6, + DXGI_FORMAT_R32G32B32_UINT = 7, + DXGI_FORMAT_R32G32B32_SINT = 8, + + DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, + DXGI_FORMAT_R16G16B16A16_FLOAT = 10, + DXGI_FORMAT_R16G16B16A16_UNORM = 11, + DXGI_FORMAT_R16G16B16A16_UINT = 12, + DXGI_FORMAT_R16G16B16A16_SNORM = 13, + DXGI_FORMAT_R16G16B16A16_SINT = 14, + + DXGI_FORMAT_R32G32_TYPELESS = 15, + DXGI_FORMAT_R32G32_FLOAT = 16, + DXGI_FORMAT_R32G32_UINT = 17, + DXGI_FORMAT_R32G32_SINT = 18, + + DXGI_FORMAT_R32G8X24_TYPELESS = 19, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, + + DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, + DXGI_FORMAT_R10G10B10A2_UNORM = 24, + DXGI_FORMAT_R10G10B10A2_UINT = 25, + + DXGI_FORMAT_R11G11B10_FLOAT = 26, + + DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, + DXGI_FORMAT_R8G8B8A8_UNORM = 28, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, + DXGI_FORMAT_R8G8B8A8_UINT = 30, + DXGI_FORMAT_R8G8B8A8_SNORM = 31, + DXGI_FORMAT_R8G8B8A8_SINT = 32, + + DXGI_FORMAT_R16G16_TYPELESS = 33, + DXGI_FORMAT_R16G16_FLOAT = 34, + DXGI_FORMAT_R16G16_UNORM = 35, + DXGI_FORMAT_R16G16_UINT = 36, + DXGI_FORMAT_R16G16_SNORM = 37, + DXGI_FORMAT_R16G16_SINT = 38, + + DXGI_FORMAT_R32_TYPELESS = 39, + DXGI_FORMAT_D32_FLOAT = 40, + DXGI_FORMAT_R32_FLOAT = 41, + DXGI_FORMAT_R32_UINT = 42, + DXGI_FORMAT_R32_SINT = 43, + + DXGI_FORMAT_R24G8_TYPELESS = 44, + DXGI_FORMAT_D24_UNORM_S8_UINT = 45, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, + DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, + + DXGI_FORMAT_R8G8_TYPELESS = 48, + DXGI_FORMAT_R8G8_UNORM = 49, + DXGI_FORMAT_R8G8_UINT = 50, + DXGI_FORMAT_R8G8_SNORM = 51, + DXGI_FORMAT_R8G8_SINT = 52, + + DXGI_FORMAT_R16_TYPELESS = 53, + DXGI_FORMAT_R16_FLOAT = 54, + DXGI_FORMAT_D16_UNORM = 55, + DXGI_FORMAT_R16_UNORM = 56, + DXGI_FORMAT_R16_UINT = 57, + DXGI_FORMAT_R16_SNORM = 58, + DXGI_FORMAT_R16_SINT = 59, + + DXGI_FORMAT_R8_TYPELESS = 60, + DXGI_FORMAT_R8_UNORM = 61, + DXGI_FORMAT_R8_UINT = 62, + DXGI_FORMAT_R8_SNORM = 63, + DXGI_FORMAT_R8_SINT = 64, + DXGI_FORMAT_A8_UNORM = 65, + + DXGI_FORMAT_R1_UNORM = 66, + + DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, + + DXGI_FORMAT_R8G8_B8G8_UNORM = 68, + DXGI_FORMAT_G8R8_G8B8_UNORM = 69, + + DXGI_FORMAT_BC1_TYPELESS = 70, + DXGI_FORMAT_BC1_UNORM = 71, + DXGI_FORMAT_BC1_UNORM_SRGB = 72, + + DXGI_FORMAT_BC2_TYPELESS = 73, + DXGI_FORMAT_BC2_UNORM = 74, + DXGI_FORMAT_BC2_UNORM_SRGB = 75, + + DXGI_FORMAT_BC3_TYPELESS = 76, + DXGI_FORMAT_BC3_UNORM = 77, + DXGI_FORMAT_BC3_UNORM_SRGB = 78, + + DXGI_FORMAT_BC4_TYPELESS = 79, + DXGI_FORMAT_BC4_UNORM = 80, + DXGI_FORMAT_BC4_SNORM = 81, + + DXGI_FORMAT_BC5_TYPELESS = 82, + DXGI_FORMAT_BC5_UNORM = 83, + DXGI_FORMAT_BC5_SNORM = 84, + + DXGI_FORMAT_B5G6R5_UNORM = 85, + DXGI_FORMAT_B5G5R5A1_UNORM = 86, + DXGI_FORMAT_B8G8R8A8_UNORM = 87, + DXGI_FORMAT_B8G8R8X8_UNORM = 88, + + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, + DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, + DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, + + DXGI_FORMAT_BC6H_TYPELESS = 94, + DXGI_FORMAT_BC6H_UF16 = 95, + DXGI_FORMAT_BC6H_SF16 = 96, + + DXGI_FORMAT_BC7_TYPELESS = 97, + DXGI_FORMAT_BC7_UNORM = 98, + DXGI_FORMAT_BC7_UNORM_SRGB = 99, + + DXGI_FORMAT_FORCE_UINT = 0xffffffffUL +}; + +struct DDSPixelFormat +{ + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t rgbBitCount; + uint32_t rBitMask; + uint32_t gBitMask; + uint32_t bBitMask; + uint32_t aBitMask; +}; + +struct DDSHeader +{ + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; + uint32_t mipMapCount; + uint32_t reserved[11]; + + DDSPixelFormat format; + + uint32_t caps1; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; +}; + +struct DDSHeader10 +{ + DXGIFormat dxgiFormat; + D3D10ResourceDimension resourceDimension; + + uint32_t miscFlag; + uint32_t arraySize; + uint32_t reserved; +}; + +} // dxinfo +} // dds + +#endif // DDS_INFO_H diff --git a/src/libraries/ddsparse/ddsparse.cpp b/src/libraries/ddsparse/ddsparse.cpp new file mode 100644 index 000000000..0046a24f3 --- /dev/null +++ b/src/libraries/ddsparse/ddsparse.cpp @@ -0,0 +1,368 @@ +/** + * Simple DDS data parser for compressed 2D textures. + * + * Copyright (c) 2013 Alexander Szpakowski. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + **/ + +#include "ddsparse.h" +#include "ddsinfo.h" + +#include + +namespace dds +{ + +using namespace dds::dxinfo; + +// Creates a packed uint representation of a FourCC code. +static inline uint32_t FourCC(char a, char b, char c, char d) +{ + uint32_t fcc = ((uint32_t) a) + | (((uint32_t) b) << 8) + | (((uint32_t) c) << 16) + | (((uint32_t) d) << 24); + + return fcc; +} + +Parser::Image::Image() + : width(0) + , height(0) + , dataSize(0) + , data(0) +{ +} + +bool Parser::isDDS(const void *data, size_t dataSize) +{ + const uint8_t *readData = (const uint8_t *) data; + ptrdiff_t offset = 0; + + // Is the data large enough to hold the DDS header? + if(dataSize < sizeof(uint32_t) + sizeof(DDSHeader)) + return false; + + // All DDS files start with "DDS ". + if((*(uint32_t *) readData) != FourCC('D','D','S',' ')) + return false; + + offset += sizeof(uint32_t); + + DDSHeader *header = (DDSHeader *) &readData[offset]; + + + // Verify header to validate DDS data. + if (header->size != sizeof(DDSHeader) || header->format.size != sizeof(DDSPixelFormat)) + return false; + + offset += sizeof(DDSHeader); + + // Check for DX10 extension. + if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FourCC('D','X','1','0'))) + { + // Data must be big enough for both headers plus the magic value. + if (dataSize < (sizeof(uint32_t) + sizeof(DDSHeader) + sizeof(DDSHeader10))) + return false; + + } + + return true; +} + +Parser::Parser(const void *data, size_t dataSize, Options opts) + : format(FORMAT_UNKNOWN) + , options(opts) +{ + parseData(data, dataSize); +} + +Parser::Parser(const Parser &other) + : format(other.format) + , options(other.options) +{ + std::vector::const_iterator it; + for (it = other.texData.begin(); it != other.texData.end(); ++it) + { + Image img = *it; + + if ((options & OPTIONS_COPY_DATA) && img.dataSize > 0) + { + uint8_t *data = 0; + + try + { + data = new uint8_t[img.dataSize]; + } + catch (std::exception &) + { + clearData(); + throw; + } + + img.data = data; + } + + texData.push_back(img); + } +} + +Parser::~Parser() +{ + // Delete any data we created. + clearData(); +} + +Format Parser::getFormat() const +{ + return format; +} + +const Parser::Image *Parser::getImageData(size_t miplevel) const +{ + if (miplevel >= texData.size()) + return 0; + + return &texData[miplevel]; +} + +size_t Parser::getNumMipmaps() const +{ + return texData.size(); +} + +bool Parser::ownsData() const +{ + return (options & OPTIONS_COPY_DATA) != 0; +} + +Format Parser::parseDDSFormat(const DDSPixelFormat &fmt) const +{ + if (fmt.flags & DDPF_FOURCC) + { + if (fmt.fourCC == FourCC('D','X','T','1')) + return FORMAT_DXT1; + else if (fmt.fourCC == FourCC('D','X','T','3')) + return FORMAT_DXT3; + else if (fmt.fourCC == FourCC('D','X','T','5')) + return FORMAT_DXT5; + else if (fmt.fourCC == FourCC('A','T','I','2')) + return FORMAT_BC5u; + } + + return FORMAT_UNKNOWN; +} + +Format Parser::parseDX10Format(DXGIFormat fmt) const +{ + Format f = FORMAT_UNKNOWN; + + switch (fmt) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + f = FORMAT_DXT1; + break; + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + f = FORMAT_DXT3; + break; + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + f = FORMAT_DXT5; + break; + case DXGI_FORMAT_BC5_SNORM: + f = FORMAT_BC5s; + break; + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + f = FORMAT_BC5u; + break; + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + f = FORMAT_BC7; + break; + case DXGI_FORMAT_BC7_UNORM_SRGB: + f = FORMAT_BC7srgb; + default: + break; + } + + return f; +} + +size_t Parser::parseImageSize(Format fmt, int width, int height) const +{ + size_t size = 0; + + switch (fmt) + { + case FORMAT_DXT1: + case FORMAT_DXT3: + case FORMAT_DXT5: + case FORMAT_BC5s: + case FORMAT_BC5u: + case FORMAT_BC7: + case FORMAT_BC7srgb: + { + int numBlocksWide = 0; + if (width > 0) + numBlocksWide = std::max(1, (width + 3) / 4); + + int numBlocksHigh = 0; + if (height > 0) + numBlocksHigh = std::max(1, (height + 3) / 4); + + int numBytesPerBlock = (fmt == FORMAT_DXT1 ? 8 : 16); + + size = numBlocksWide * numBytesPerBlock * numBlocksHigh; + } + break; + default: + break; + } + + return size; +} + +bool Parser::parseTexData(const uint8_t *data, size_t dataSize, Format fmt, int w, int h, int mips) +{ + size_t offset = 0; + + for (int i = 0; i < mips; i++) + { + Image img; + img.width = w; + img.height = h; + + img.dataSize = parseImageSize(fmt, img.width, img.height); + + // Make sure the data size is valid. + if (img.dataSize == 0 || (offset + img.dataSize) > dataSize) + { + // Clean up any data we allocated previously. + clearData(); + return false; + } + + // Create our own copy of the data, if requested. + if (options & OPTIONS_COPY_DATA) + { + uint8_t *newData = 0; + + try + { + newData = new uint8_t[img.dataSize]; + } + catch (std::exception &) + { + // Clean up before throwing. + clearData(); + throw; + } + + memcpy(newData, &data[offset], img.dataSize); + img.data = newData; + } + else + img.data = &data[offset]; + + texData.push_back(img); + + // Move to the next mip level. + offset += img.dataSize; + + w = std::max(w / 2, 1); + h = std::max(h / 2, 1); + } + + return true; +} + +bool Parser::parseData(const void *data, size_t dataSize) +{ + if (!isDDS(data, dataSize)) + return false; + + const uint8_t *readData = (const uint8_t *) data; + ptrdiff_t offset = sizeof(uint32_t); + + DDSHeader *header = (DDSHeader *) &readData[offset]; + offset += sizeof(DDSHeader); + + + // Check for DX10 extension. + if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FourCC('D','X','1','0'))) + { + DDSHeader10 *header10 = (DDSHeader10 *) &readData[offset]; + offset += sizeof(DDSHeader10); + + // We can't deal with 1D/3D textures. + switch (header10->resourceDimension) + { + case D3D10_RESOURCE_DIMENSION_TEXTURE2D: + case D3D10_RESOURCE_DIMENSION_UNKNOWN: + break; + default: + return false; + } + + // We also can't deal with texture arrays and cubemaps. + if (header10->arraySize > 1) + return false; + + format = parseDX10Format(header10->dxgiFormat); + } + else + format = parseDDSFormat(header->format); + + + if (format == FORMAT_UNKNOWN) + return false; + + + int w = header->width; + int h = header->height; + + int mips = std::max((int) header->mipMapCount, 1); + + return parseTexData(&readData[offset], dataSize - offset, format, w, h, mips); +} + +void Parser::clearData() +{ + if (options & OPTIONS_COPY_DATA) + { + // Delete any data we created. + std::vector::iterator it; + for (it = texData.begin(); it != texData.end(); ++it) + { + delete[] it->data; + it->data = 0; + } + } + + texData.clear(); +} + +} // dds diff --git a/src/libraries/ddsparse/ddsparse.h b/src/libraries/ddsparse/ddsparse.h new file mode 100644 index 000000000..d0c3f89d9 --- /dev/null +++ b/src/libraries/ddsparse/ddsparse.h @@ -0,0 +1,139 @@ +/** + * Simple DDS data parser for compressed 2D textures. + * + * Copyright (c) 2013 Alexander Szpakowski. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + **/ + +#ifndef DDS_PARSE_H +#define DDS_PARSE_H + +#include "ddsinfo.h" + +#include +#include + +#include +#include + +namespace dds +{ + +// Supported DDS formats. +enum Format +{ + FORMAT_DXT1, + FORMAT_DXT3, + FORMAT_DXT5, + FORMAT_BC5s, // Signed. + FORMAT_BC5u, // Unsigned. + FORMAT_BC7, + FORMAT_BC7srgb, // sRGB color space. + FORMAT_UNKNOWN +}; + +class Parser +{ +public: + + enum Options + { + OPTIONS_NONE = 0x00, + OPTIONS_COPY_DATA = 0x01 // Copy texture data internally when parsing. + }; + + // Represents a single mipmap level of a texture. + struct Image + { + int width; + int height; + size_t dataSize; + const uint8_t *data; + + Image(); + }; + + /** + * Determines whether the input byte data is a valid DDS representation. + **/ + static bool isDDS(const void *data, size_t dataSize); + + /** + * Constructor. + * Attempts to parse byte data as DDS. May throw std::bad_alloc if out of + * memory. + * @param data The byte data to parse. + * @param dataSize The size in bytes of the data. + * @param options Any optional settings (see above.) + **/ + Parser(const void *data, size_t dataSize, Options opts = OPTIONS_NONE); + Parser(const Parser &other); + + ~Parser(); + + /** + * Gets the format of this texture. + **/ + Format getFormat() const; + + /** + * Gets the data of this texture at a mipmap level. Mipmap level 0 + * represents the base image. + * @param miplevel The mipmap level to get the data of. + * @return Pointer to the image data, or 0 if miplevel is not within the + * range of [0, numMipmaps). + **/ + const Image *getImageData(size_t miplevel = 0) const; + + /** + * Gets the number of mipmap levels in this texture. + * Includes the base mip level. + **/ + size_t getNumMipmaps() const; + + /** + * Gets whether this Parser is using an internal copy of the texture data. + * Accessing texture data from a Parser which hasn't internally copied the + * DDS data will result in undefined behaviour if the original data is deleted. + **/ + bool ownsData() const; + +private: + + Format parseDDSFormat(const dxinfo::DDSPixelFormat &fmt) const; + Format parseDX10Format(dxinfo::DXGIFormat fmt) const; + size_t parseImageSize(Format fmt, int width, int height) const; + + bool parseTexData(const uint8_t *data, size_t dataSize, Format fmt, int w, int h, int mips); + + bool parseData(const void *data, size_t dataSize); + + // Delete any heap data created by this object. + void clearData(); + + std::vector texData; + Format format; + Options options; + +}; // Parser + +} // dds + +#endif // DDS_PARSE_H diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 682650568..69ef21bf3 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -163,6 +163,9 @@ StringMap::Entry Graphics::suppor { "npot", Graphics::SUPPORT_NPOT }, { "subtractive", Graphics::SUPPORT_SUBTRACTIVE }, { "mipmap", Graphics::SUPPORT_MIPMAP }, + { "dxt", Graphics::SUPPORT_DXT }, + { "bc5", Graphics::SUPPORT_BC5 }, + { "bc7", Graphics::SUPPORT_BC7 }, }; StringMap Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries)); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index e2c73ae62..435a014f2 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -92,6 +92,9 @@ public: SUPPORT_NPOT, SUPPORT_SUBTRACTIVE, SUPPORT_MIPMAP, + SUPPORT_DXT, + SUPPORT_BC5, + SUPPORT_BC7, SUPPORT_MAX_ENUM }; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index d5e50e31c..c90af58a2 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -373,6 +373,29 @@ Image *Graphics::newImage(love::image::ImageData *data) return image; } +Image *Graphics::newImage(love::image::CompressedData *cdata) +{ + // Create the image. + Image *image = new Image(cdata); + bool success; + try + { + success = image->load(); + } + catch(love::Exception &) + { + image->release(); + throw; + } + if (!success) + { + image->release(); + return 0; + } + + return image; +} + Quad *Graphics::newQuad(float x, float y, float w, float h, float sw, float sh) { Quad::Viewport v; diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 9d79f57a2..5890e1761 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -251,8 +251,8 @@ public: /** * Creates an Image object with padding and/or optimization. **/ - Image *newImage(love::filesystem::File *file); Image *newImage(love::image::ImageData *data); + Image *newImage(love::image::CompressedData *cdata); /** * Creates a Quad object. diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 4af318e8d..a14b5f46e 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -39,43 +39,39 @@ Image::FilterMode Image::defaultMipmapFilter = Image::FILTER_NONE; float Image::defaultMipmapSharpness = 0.0f; Image::Image(love::image::ImageData *data) - : width((float)(data->getWidth())) + : cdata(0) + , width((float)(data->getWidth())) , height((float)(data->getHeight())) , texture(0) , mipmapSharpness(defaultMipmapSharpness) , mipmapsCreated(false) + , isCompressed(false) { data->retain(); this->data = data; + preload(); +} - memset(vertices, 255, sizeof(vertex)*4); - - vertices[0].x = 0; - vertices[0].y = 0; - vertices[1].x = 0; - vertices[1].y = height; - vertices[2].x = width; - vertices[2].y = height; - vertices[3].x = width; - vertices[3].y = 0; - - vertices[0].s = 0; - vertices[0].t = 0; - vertices[1].s = 0; - vertices[1].t = 1; - vertices[2].s = 1; - vertices[2].t = 1; - vertices[3].s = 1; - vertices[3].t = 0; - - filter = getDefaultFilter(); - filter.mipmap = defaultMipmapFilter; +Image::Image(love::image::CompressedData *cdata) + : data(0) + , width((float)(cdata->getWidth(0))) + , height((float)(cdata->getHeight(0))) + , texture(0) + , mipmapSharpness(defaultMipmapSharpness) + , mipmapsCreated(false) + , isCompressed(true) +{ + cdata->retain(); + this->cdata = cdata; + preload(); } Image::~Image() { if (data != 0) data->release(); + if (cdata != 0) + cdata->release(); unload(); } @@ -155,18 +151,46 @@ void Image::checkMipmapsCreated() if (mipmapsCreated || (filter.mipmap != FILTER_NEAREST && filter.mipmap != FILTER_LINEAR)) return; - if (!hasMipmapSupport()) + if (!(isCompressed && cdata) && !hasMipmapSupport()) throw love::Exception("Mipmap filtering is not supported on this system."); // Some old drivers claim support for NPOT textures, but fail when creating mipmaps. // we can't detect which systems will do this, so we fail gracefully for all NPOT images. int w = int(width), h = int(height); - if (w != next_p2(w) || h != next_p2(h)) + if (!isCompressed && (w != next_p2(w) || h != next_p2(h))) throw love::Exception("Cannot create mipmaps: image does not have power of two dimensions."); bind(); - if (hasNpot() && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)) + if (isCompressed && cdata && hasCompressedTextureSupport(cdata->getType())) + { + int numMipmaps = cdata->getNumMipmaps(); + + // We have to inform OpenGL if the image doesn't have all mipmap levels. + if (GLEE_VERSION_1_2 || GLEE_SGIS_texture_lod) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMipmaps - 1); + else if (cdata->getWidth(numMipmaps-1) > 1 || cdata->getHeight(numMipmaps-1) > 1) + { + // Telling OpenGL to ignore certain levels isn't always supported. + throw love::Exception("Cannot load mipmaps: " + "compressed image does not have all required levels."); + } + + GLenum format = getCompressedFormat(cdata->getType()); + + for (int i = 1; i < numMipmaps; i++) + { + glCompressedTexImage2DARB(GL_TEXTURE_2D, + i, + format, + cdata->getWidth(i), + cdata->getHeight(i), + 0, + GLsizei(cdata->getSize(i)), + cdata->getData(i)); + } + } + else if (data && hasNpot() && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)) { // AMD/ATI drivers have several bugs when generating mipmaps, // re-uploading the entire base image seems to be required. @@ -184,7 +208,7 @@ void Image::checkMipmapsCreated() glEnable(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D); } - else + else if (data) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexSubImage2D(GL_TEXTURE_2D, @@ -197,6 +221,8 @@ void Image::checkMipmapsCreated() GL_UNSIGNED_BYTE, data->getData()); } + else + return; mipmapsCreated = true; } @@ -255,6 +281,32 @@ void Image::bind() const bindTexture(texture); } +void Image::preload() +{ + memset(vertices, 255, sizeof(vertex)*4); + + vertices[0].x = 0; + vertices[0].y = 0; + vertices[1].x = 0; + vertices[1].y = height; + vertices[2].x = width; + vertices[2].y = height; + vertices[3].x = width; + vertices[3].y = 0; + + vertices[0].s = 0; + vertices[0].t = 0; + vertices[1].s = 0; + vertices[1].t = 1; + vertices[2].s = 1; + vertices[2].t = 1; + vertices[3].s = 1; + vertices[3].t = 0; + + filter = getDefaultFilter(); + filter.mipmap = defaultMipmapFilter; +} + bool Image::load() { return loadVolatile(); @@ -267,6 +319,18 @@ void Image::unload() bool Image::loadVolatile() { + if (isCompressed && cdata && !hasCompressedTextureSupport(cdata->getType())) + { + const char *str; + if (image::CompressedData::getConstant(cdata->getType(), str)) + { + throw love::Exception("Cannot create image: " + "%s compressed images are not supported on this system.", str); + } + else + throw love::Exception("cannot create image: format is not supported on this system."); + } + if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f) glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness); @@ -296,25 +360,47 @@ bool Image::loadVolatilePOT() while (glGetError() != GL_NO_ERROR); // clear errors - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA8, - (GLsizei)p2width, - (GLsizei)p2height, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - 0); + if (isCompressed && cdata) + { + if (s != 1.0f || t != 1.0f) + { + throw love::Exception("Cannot create image: " + "NPOT compressed images are not supported on this system."); + } - glTexSubImage2D(GL_TEXTURE_2D, - 0, - 0, - 0, - (GLsizei)width, - (GLsizei)height, - GL_RGBA, - GL_UNSIGNED_BYTE, - data->getData()); + GLenum format = getCompressedFormat(cdata->getType()); + + glCompressedTexImage2DARB(GL_TEXTURE_2D, + 0, + format, + cdata->getWidth(0), + cdata->getHeight(0), + 0, + GLsizei(cdata->getSize(0)), + cdata->getData(0)); + } + else if (data) + { + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA8, + (GLsizei)p2width, + (GLsizei)p2height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + 0); + + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + (GLsizei)width, + (GLsizei)height, + GL_RGBA, + GL_UNSIGNED_BYTE, + data->getData()); + } if (glGetError() != GL_NO_ERROR) throw love::Exception("Cannot create image: size may be too large for this system."); @@ -337,15 +423,30 @@ bool Image::loadVolatileNPOT() while (glGetError() != GL_NO_ERROR); // clear errors - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA8, - (GLsizei)width, - (GLsizei)height, - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - data->getData()); + if (isCompressed && cdata) + { + GLenum format = getCompressedFormat(cdata->getType()); + glCompressedTexImage2DARB(GL_TEXTURE_2D, + 0, + format, + cdata->getWidth(0), + cdata->getHeight(0), + 0, + GLsizei(cdata->getSize(0)), + cdata->getData(0)); + } + else if (data) + { + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA8, + (GLsizei)width, + (GLsizei)height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + data->getData()); + } if (glGetError() != GL_NO_ERROR) throw love::Exception("Cannot create image: size may be too large for this system."); @@ -407,6 +508,29 @@ Image::FilterMode Image::getDefaultMipmapFilter() return defaultMipmapFilter; } +GLenum Image::getCompressedFormat(image::CompressedData::TextureType type) const +{ + switch (type) + { + case image::CompressedData::TYPE_DXT1: + return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case image::CompressedData::TYPE_DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case image::CompressedData::TYPE_DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case image::CompressedData::TYPE_BC5s: + return GL_COMPRESSED_SIGNED_RG_RGTC2; + case image::CompressedData::TYPE_BC5u: + return GL_COMPRESSED_RG_RGTC2; + case image::CompressedData::TYPE_BC7: + return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case image::CompressedData::TYPE_BC7srgb: + return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + default: + return GL_RGBA8; + } +} + bool Image::hasNpot() { return GLEE_VERSION_2_0 || GLEE_ARB_texture_non_power_of_two; @@ -427,6 +551,37 @@ bool Image::hasMipmapSharpnessSupport() return GLEE_VERSION_1_4 || GLEE_EXT_texture_lod_bias; } +bool Image::hasCompressedTextureSupport() +{ + return GLEE_VERSION_1_3 || GLEE_ARB_texture_compression; +} + +bool Image::hasCompressedTextureSupport(image::CompressedData::TextureType type) +{ + if (!hasCompressedTextureSupport()) + return false; + + switch (type) + { + case image::CompressedData::TYPE_DXT1: + case image::CompressedData::TYPE_DXT3: + case image::CompressedData::TYPE_DXT5: + return GLEE_EXT_texture_compression_s3tc; + + case image::CompressedData::TYPE_BC5s: + case image::CompressedData::TYPE_BC5u: + return (GLEE_VERSION_3_0 || GLEE_ARB_texture_compression_rgtc); + + case image::CompressedData::TYPE_BC7: + case image::CompressedData::TYPE_BC7srgb: + return (GLEE_VERSION_4_2 || GLEE_ARB_texture_compression_bptc); + + default: + return false; + + } +} + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 8b3295fb9..ba3d2d885 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -26,6 +26,7 @@ #include "common/math.h" #include "common/config.h" #include "image/ImageData.h" +#include "image/CompressedData.h" #include "graphics/Image.h" // OpenGL @@ -52,10 +53,17 @@ public: * Creates a new Image. Not that anything is ready to use * before load is called. * - * @param file The file from which to load the image. + * @param data The data from which to load the image. **/ Image(love::image::ImageData *data); + /** + * Creates a new Image with compressed image data. + * + * @param cdata The compressed data from which to load the image. + **/ + Image(love::image::CompressedData *cdata); + /** * Destructor. Deletes the hardware texture and other resources. **/ @@ -126,6 +134,9 @@ public: static bool hasMipmapSupport(); static bool hasMipmapSharpnessSupport(); + static bool hasCompressedTextureSupport(); + static bool hasCompressedTextureSupport(image::CompressedData::TextureType type); + private: void drawv(const Matrix &t, const vertex *v) const; @@ -138,6 +149,9 @@ private: // The ImageData from which the texture is created. love::image::ImageData *data; + // Or the Compressed Image Data from which the texture is created. + love::image::CompressedData *cdata; + // Width and height of the hardware texture. float width, height; @@ -153,12 +167,16 @@ private: // True if mipmaps have been created for this Image. bool mipmapsCreated; + bool isCompressed; + // The image's filter mode Image::Filter filter; // The image's wrap mode Image::Wrap wrap; + void preload(); + bool loadVolatilePOT(); bool loadVolatileNPOT(); @@ -169,6 +187,8 @@ private: static FilterMode defaultMipmapFilter; static float defaultMipmapSharpness; + GLenum getCompressedFormat(image::CompressedData::TextureType type) const; + }; // Image } // opengl diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 650fb8a72..629e634ad 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -97,6 +97,14 @@ void initializeContext() glUnmapBufferARB = (GLEEPFNGLUNMAPBUFFERARBPROC) glUnmapBuffer; } + // Same deal for compressed textures. + if (GLEE_VERSION_1_3 && !GLEE_ARB_texture_compression) + { + glCompressedTexImage2DARB = (GLEEPFNGLCOMPRESSEDTEXIMAGE2DARBPROC) glCompressedTexImage2D; + glCompressedTexSubImage2DARB = (GLEEPFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) glCompressedTexSubImage2D; + glGetCompressedTexImageARB = (GLEEPFNGLGETCOMPRESSEDTEXIMAGEARBPROC) glGetCompressedTexImage; + } + if (GLEE_EXT_texture_filter_anisotropic) glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); else diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index d46bf743d..9813ecb22 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -269,21 +269,45 @@ int w_setInvertedStencil(lua_State *L) int w_newImage(lua_State *L) { + love::image::ImageData *data = 0; + love::image::CompressedData *cdata = 0; + // Convert to File, if necessary. if (lua_isstring(L, 1)) luax_convobj(L, 1, "filesystem", "newFile"); - // Convert to ImageData, if necessary. + // Convert to ImageData/CompressedData, if necessary. if (luax_istype(L, 1, FILESYSTEM_FILE_T)) - luax_convobj(L, 1, "image", "newImageData"); + { + // Determine whether to convert to ImageData or CompressedData. + luax_getfunction(L, "image", "isCompressed"); + lua_pushvalue(L, 1); + lua_call(L, 1, 1); - love::image::ImageData *data = luax_checktype(L, 1, "ImageData", IMAGE_IMAGE_DATA_T); + bool compressed = luax_toboolean(L, -1); + lua_pop(L, 1); + + if (compressed) + luax_convobj(L, 1, "image", "newCompressedData"); + else + luax_convobj(L, 1, "image", "newImageData"); + } + + if (luax_istype(L, 1, IMAGE_COMPRESSED_DATA_T)) + cdata = luax_checktype(L, 1, "CompressedData", IMAGE_COMPRESSED_DATA_T); + else + data = luax_checktype(L, 1, "ImageData", IMAGE_IMAGE_DATA_T); // Create the image. Image *image = 0; try { - image = instance->newImage(data); + if (cdata) + image = instance->newImage(cdata); + else if (data) + image = instance->newImage(data); + else + throw love::Exception("Error creating image."); } catch(love::Exception &e) { @@ -1110,6 +1134,18 @@ int w_isSupported(lua_State *L) if (!Image::hasMipmapSupport()) supported = false; break; + case Graphics::SUPPORT_DXT: + if (!Image::hasCompressedTextureSupport(image::CompressedData::TYPE_DXT5)) + supported = false; + break; + case Graphics::SUPPORT_BC5: + if (!Image::hasCompressedTextureSupport(image::CompressedData::TYPE_BC5u)) + supported = false; + break; + case Graphics::SUPPORT_BC7: + if (!Image::hasCompressedTextureSupport(image::CompressedData::TYPE_BC7)) + supported = false; + break; default: supported = false; } diff --git a/src/modules/image/CompressedData.cpp b/src/modules/image/CompressedData.cpp new file mode 100644 index 000000000..236863e85 --- /dev/null +++ b/src/modules/image/CompressedData.cpp @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2006-2013 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 "CompressedData.h" + +namespace love +{ +namespace image +{ + +CompressedData::CompressedData() + : type(TYPE_MAX_ENUM) +{ + +} + +CompressedData::~CompressedData() +{ + +} + +int CompressedData::getSize() const +{ + size_t totalsize = sizeof(MipmapInfo) * dataMipmapInfo.size(); + + for (size_t i = 0; i < dataMipmapInfo.size(); i++) + totalsize += dataMipmapInfo[i].size; + + return totalsize; +} + +void *CompressedData::getData() const +{ + // ? + return (void *) &dataMipmapInfo[0].data[0]; +} + +int CompressedData::getNumMipmaps() const +{ + return dataMipmapInfo.size(); +} + +int CompressedData::getSize(int miplevel) const +{ + checkMipmapLevelExists(miplevel); + + return dataMipmapInfo[miplevel].size; +} + +void *CompressedData::getData(int miplevel) const +{ + checkMipmapLevelExists(miplevel); + + return (void *) &dataMipmapInfo[miplevel].data[0]; +} + +int CompressedData::getWidth(int miplevel) const +{ + checkMipmapLevelExists(miplevel); + + return dataMipmapInfo[miplevel].width; +} + +int CompressedData::getHeight(int miplevel) const +{ + checkMipmapLevelExists(miplevel); + + return dataMipmapInfo[miplevel].height; +} + +CompressedData::TextureType CompressedData::getType() const +{ + return type; +} + +void CompressedData::checkMipmapLevelExists(int miplevel) const +{ + if (miplevel < 0 || miplevel >= dataMipmapInfo.size()) + throw love::Exception("Mipmap level %d does not exist", miplevel); +} + +bool CompressedData::getConstant(const char *in, CompressedData::TextureType &out) +{ + return types.find(in, out); +} + +bool CompressedData::getConstant(CompressedData::TextureType in, const char *&out) +{ + return types.find(in, out); +} + +StringMap::Entry CompressedData::typeEntries[] = +{ + {"dxt1", CompressedData::TYPE_DXT1}, + {"dxt3", CompressedData::TYPE_DXT3}, + {"dxt5", CompressedData::TYPE_DXT5}, + {"bc5s", CompressedData::TYPE_BC5s}, + {"bc5u", CompressedData::TYPE_BC5u}, + {"bc7", CompressedData::TYPE_BC7}, + {"bc7srgb", CompressedData::TYPE_BC7srgb}, +}; + +StringMap CompressedData::types(CompressedData::typeEntries, sizeof(CompressedData::typeEntries)); + +} // image +} // love diff --git a/src/modules/image/CompressedData.h b/src/modules/image/CompressedData.h new file mode 100644 index 000000000..2ac3b9021 --- /dev/null +++ b/src/modules/image/CompressedData.h @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2006-2013 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_COMPRESSED_DATA_H +#define LOVE_IMAGE_COMPRESSED_DATA_H + +// LOVE +#include "common/Data.h" +#include "common/StringMap.h" +#include "thread/threads.h" + +using love::thread::Mutex; + +// STL +#include + +namespace love +{ +namespace image +{ + +// +class CompressedData : public Data +{ +public: + + // + enum TextureType + { + TYPE_DXT1, + TYPE_DXT3, + TYPE_DXT5, + TYPE_BC5s, + TYPE_BC5u, + TYPE_BC7, + TYPE_BC7srgb, + TYPE_MAX_ENUM + }; + + CompressedData(); + + virtual ~CompressedData(); + + // Implements Data. + virtual void *getData() const; + virtual int getSize() const; + + /** + * Gets the number of mipmaps in this CompressedData. + * Includes the base image level. + **/ + int getNumMipmaps() const; + + /** + * Gets the size in bytes of the sub-image at the specified mipmap level. + **/ + int getSize(int miplevel) const; + + /** + * Gets the byte data of the sub-image at the specified mipmap level. + **/ + void *getData(int miplevel) const; + + /** + * Gets the width of the sub-image at the specified mipmap level. + **/ + int getWidth(int miplevel) const; + + /** + * Gets the height of the sub-image at the specified mipmap level. + **/ + int getHeight(int miplevel) const; + + /** + * Gets the type of the compressed data. + **/ + TextureType getType() const; + + static bool getConstant(const char *in, TextureType &out); + static bool getConstant(TextureType in, const char *&out); + +protected: + + struct MipmapInfo + { + size_t size; + int width, height; + std::vector data; + }; + + TextureType type; + + // Texture info for each mipmap level. + std::vector dataMipmapInfo; + + void checkMipmapLevelExists(int miplevel) const; + +private: + + static StringMap::Entry typeEntries[]; + static StringMap types; + +}; // CompressedData + +} // image +} // love + +#endif // LOVE_IMAGE_COMPRESSED_DATA_H diff --git a/src/modules/image/Image.h b/src/modules/image/Image.h index 533798b85..c9aefadf3 100644 --- a/src/modules/image/Image.h +++ b/src/modules/image/Image.h @@ -26,6 +26,7 @@ #include "common/Module.h" #include "filesystem/File.h" #include "ImageData.h" +#include "CompressedData.h" namespace love { @@ -77,6 +78,26 @@ public: **/ virtual ImageData *newImageData(int width, int height, void *data) = 0; + /** + * + **/ + virtual CompressedData *newCompressedData(love::filesystem::File *file) = 0; + + /** + * + **/ + virtual CompressedData *newCompressedData(Data *data) = 0; + + /** + * + **/ + virtual bool isCompressed(love::filesystem::File *file) = 0; + + /** + * + **/ + virtual bool isCompressed(Data *data) = 0; + }; // Image } // image diff --git a/src/modules/image/ImageData.cpp b/src/modules/image/ImageData.cpp index 1f250db76..c7a372a39 100644 --- a/src/modules/image/ImageData.cpp +++ b/src/modules/image/ImageData.cpp @@ -39,14 +39,19 @@ ImageData::~ImageData() delete mutex; } +int ImageData::getSize() const +{ + return getWidth()*getHeight()*sizeof(pixel); +} + void *ImageData::getData() const { return data; } -int ImageData::getSize() const +bool ImageData::inside(int x, int y) const { - return getWidth()*getHeight()*sizeof(pixel); + return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); } int ImageData::getWidth() const @@ -59,11 +64,6 @@ int ImageData::getHeight() const return height; } -bool ImageData::inside(int x, int y) const -{ - return x >= 0 && x < getWidth() && y >= 0 && y < getHeight(); -} - void ImageData::setPixel(int x, int y, pixel c) { if (!inside(x, y)) diff --git a/src/modules/image/ImageData.h b/src/modules/image/ImageData.h index 653a89193..a842ce5e8 100644 --- a/src/modules/image/ImageData.h +++ b/src/modules/image/ImageData.h @@ -45,22 +45,6 @@ struct pixel **/ class ImageData : public Data { -protected: - - // The width of the image data. - int width; - - // The height of the image data. - int height; - - // The actual data. - unsigned char *data; - - // We need to be thread-safe - // so we lock when we're accessing our - // data - Mutex *mutex; - public: enum Format @@ -137,10 +121,27 @@ public: virtual void encode(love::filesystem::File *f, Format format) = 0; // Implements Data. - void *getData() const; - int getSize() const; + virtual void *getData() const; + virtual int getSize() const; + +protected: + + // The width of the image data. + int width; + + // The height of the image data. + int height; + + // The actual data. + unsigned char *data; + + // We need to be thread-safe + // so we lock when we're accessing our + // data + Mutex *mutex; private: + static StringMap::Entry formatEntries[]; static StringMap formats; diff --git a/src/modules/image/devil/CompressedData.cpp b/src/modules/image/devil/CompressedData.cpp new file mode 100644 index 000000000..831dbb8e2 --- /dev/null +++ b/src/modules/image/devil/CompressedData.cpp @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2006-2013 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 "CompressedData.h" + +namespace love +{ +namespace image +{ +namespace devil +{ + +CompressedData::CompressedData(Data *data) +{ + load(data); +} + +CompressedData::CompressedData(love::filesystem::File *file) +{ + Data *data = file->read(); + try + { + load(data); + } + catch (love::Exception &) + { + data->release(); + throw; + } +} + +CompressedData::~CompressedData() +{ + +} + +bool CompressedData::convertFormat(dds::Format ddsformat) +{ + switch (ddsformat) + { + case dds::FORMAT_DXT1: + type = TYPE_DXT1; + break; + case dds::FORMAT_DXT3: + type = TYPE_DXT3; + break; + case dds::FORMAT_DXT5: + type = TYPE_DXT5; + break; + case dds::FORMAT_BC5s: + type = TYPE_BC5s; + break; + case dds::FORMAT_BC5u: + type = TYPE_BC5u; + break; + case dds::FORMAT_BC7: + type = TYPE_BC7; + break; + case dds::FORMAT_BC7srgb: + type = TYPE_BC7srgb; + break; + default: + return false; + } + + return true; +} + +void CompressedData::load(Data *data) +{ + if (!dds::Parser::isDDS(data->getData(), data->getSize())) + throw love::Exception("Could not decode compressed data (not a DDS file?)"); + + try + { + dds::Parser parser(data->getData(), data->getSize()); + + dds::Format format = parser.getFormat(); + + if (format == dds::FORMAT_UNKNOWN || !convertFormat(format)) + throw love::Exception("Could not parse compressed data: Unsupported format."); + + if (parser.getNumMipmaps() == 0) + throw love::Exception("Could not parse compressed data: No readable texture data."); + + for (size_t i = 0; i < parser.getNumMipmaps(); i++) + { + const dds::Parser::Image *img = parser.getImageData(i); + + MipmapInfo mip; + + mip.width = img->width; + mip.height = img->height; + mip.size = img->dataSize; + + mip.data.resize(mip.size); + memcpy(&mip.data[0], img->data, mip.size); + + dataMipmapInfo.push_back(mip); + } + + } + catch (std::exception &e) + { + throw love::Exception(e.what()); + } +} + +bool CompressedData::isCompressed(const Data *data) +{ + return dds::Parser::isDDS(data->getData(), data->getSize()); +} + +} // devil +} // image +} // love diff --git a/src/modules/image/devil/CompressedData.h b/src/modules/image/devil/CompressedData.h new file mode 100644 index 000000000..2a8eb7799 --- /dev/null +++ b/src/modules/image/devil/CompressedData.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2006-2013 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_DEVIL_COMPRESSED_DATA_H +#define LOVE_DEVIL_COMPRESSED_DATA_H + +// LOVE +#include "filesystem/File.h" +#include "image/CompressedData.h" + +// dds parser +#include "ddsparse/ddsparse.h" + +namespace love +{ +namespace image +{ +namespace devil +{ + +class CompressedData : public love::image::CompressedData +{ +public: + + CompressedData(love::filesystem::File *file); + CompressedData(Data *data); + + virtual ~CompressedData(); + + static bool isCompressed(const Data *data); + +private: + + bool convertFormat(dds::Format ddsformat); + void load(Data *data); + +}; // CompressedData + +} // devil +} // image +} // love + +#endif // LOVE_DEVIL_COMPRESSED_DATA_H diff --git a/src/modules/image/devil/Image.cpp b/src/modules/image/devil/Image.cpp index f36945cb2..eba49f71e 100644 --- a/src/modules/image/devil/Image.cpp +++ b/src/modules/image/devil/Image.cpp @@ -21,6 +21,7 @@ #include "Image.h" #include "ImageData.h" +#include "CompressedData.h" // DevIL #include @@ -32,6 +33,11 @@ namespace image namespace devil { +const std::string Image::compressedExts[] = +{ + ".dds", "" +}; + Image::Image() { ilInit(); @@ -69,6 +75,47 @@ love::image::ImageData *Image::newImageData(int width, int height, void *data) return new ImageData(width, height, data); } +love::image::CompressedData *Image::newCompressedData(love::filesystem::File *file) +{ + return new CompressedData(file); +} + +love::image::CompressedData *Image::newCompressedData(love::Data *data) +{ + return new CompressedData(data); +} + +bool Image::isCompressed(love::filesystem::File *file) +{ + bool hasExt = false; + + // Check whether the file has an extension known to contain compressed data. + const std::string &ext = file->getExtension(); + for (int i = 0; !(compressedExts[i].empty()); i++) + { + if (compressedExts[i].compare(ext)) + { + hasExt = true; + break; + } + } + + if (!hasExt) + return false; + + // Check whether the actual data is compressed. + Data *data = file->read(); + bool compressed = CompressedData::isCompressed(data); + data->release(); + + return compressed; +} + +bool Image::isCompressed(love::Data *data) +{ + return CompressedData::isCompressed(data); +} + } // devil } // image } // love diff --git a/src/modules/image/devil/Image.h b/src/modules/image/devil/Image.h index 6a03aea99..fce3f722b 100644 --- a/src/modules/image/devil/Image.h +++ b/src/modules/image/devil/Image.h @@ -24,6 +24,9 @@ // LOVE #include "image/Image.h" +// STL +#include + namespace love { namespace image @@ -46,6 +49,14 @@ public: love::image::ImageData *newImageData(int width, int height); love::image::ImageData *newImageData(int width, int height, void *data); + love::image::CompressedData *newCompressedData(love::filesystem::File *file); + love::image::CompressedData *newCompressedData(Data *data); + + bool isCompressed(love::filesystem::File *file); + bool isCompressed(Data *data); + + static const std::string compressedExts[]; + }; // Image } // devil diff --git a/src/modules/image/wrap_CompressedData.cpp b/src/modules/image/wrap_CompressedData.cpp new file mode 100644 index 000000000..4a9c988b9 --- /dev/null +++ b/src/modules/image/wrap_CompressedData.cpp @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2006-2013 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 "wrap_CompressedData.h" +#include "common/wrap_Data.h" + +namespace love +{ +namespace image +{ + +CompressedData *luax_checkcompresseddata(lua_State *L, int idx) +{ + return luax_checktype(L, idx, "CompressedData", IMAGE_COMPRESSED_DATA_T); +} + +int w_CompressedData_getWidth(lua_State *L) +{ + CompressedData *t = luax_checkcompresseddata(L, 1); + int miplevel = luaL_optinteger(L, 2, 1); + int width = 0; + try + { + width = t->getWidth(miplevel >= 1 ? miplevel - 1 : 0); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } + lua_pushinteger(L, width); + return 1; +} + +int w_CompressedData_getHeight(lua_State *L) +{ + CompressedData *t = luax_checkcompresseddata(L, 1); + int miplevel = luaL_optinteger(L, 2, 1); + int height = 0; + try + { + height = t->getHeight(miplevel >= 1 ? miplevel - 1 : 0); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } + lua_pushinteger(L, height); + return 1; +} + +int w_CompressedData_getDimensions(lua_State *L) +{ + CompressedData *t = luax_checkcompresseddata(L, 1); + int miplevel = luaL_optinteger(L, 2, 1); + int width = 0, height = 0; + try + { + width = t->getWidth(miplevel >= 1 ? miplevel - 1 : 0); + height = t->getHeight(miplevel >= 1 ? miplevel - 1 : 0); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } + lua_pushinteger(L, width); + lua_pushinteger(L, height); + return 2; +} + +int w_CompressedData_getNumMipmaps(lua_State *L) +{ + CompressedData *t = luax_checkcompresseddata(L, 1); + lua_pushinteger(L, t->getNumMipmaps()); + return 1; +} + +int w_CompressedData_getType(lua_State *L) +{ + CompressedData *t = luax_checkcompresseddata(L, 1); + + image::CompressedData::TextureType type = t->getType(); + const char *str; + + if (image::CompressedData::getConstant(type, str)) + lua_pushstring(L, str); + else + lua_pushstring(L, "unknown"); + + return 1; +} + +static const luaL_Reg functions[] = +{ + // Data + { "getPointer", w_Data_getPointer }, + { "getSize", w_Data_getSize }, + + { "getWidth", w_CompressedData_getWidth }, + { "getHeight", w_CompressedData_getHeight }, + { "getDimensions", w_CompressedData_getDimensions }, + { "getNumMipmaps", w_CompressedData_getNumMipmaps }, + { "getType", w_CompressedData_getType }, + { 0, 0 }, +}; + +extern "C" int luaopen_compresseddata(lua_State *L) +{ + return luax_register_type(L, "CompressedData", functions); +} + +} // image +} // love diff --git a/src/modules/image/wrap_CompressedData.h b/src/modules/image/wrap_CompressedData.h new file mode 100644 index 000000000..f3e87d698 --- /dev/null +++ b/src/modules/image/wrap_CompressedData.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2006-2013 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_WRAP_COMRESSED_DATA_H +#define LOVE_IMAGE_WRAP_COMRESSED_DATA_H + +// LOVE +#include "common/runtime.h" +#include "CompressedData.h" + +namespace love +{ +namespace image +{ + +CompressedData *luax_checkcompresseddata(lua_State *L, int idx); +int w_CompressedData_getWidth(lua_State *L); +int w_CompressedData_getHeight(lua_State *L); +int w_CompressedData_getDimensions(lua_State *L); +int w_CompressedData_getNumMipmaps(lua_State *L); +int w_CompressedData_getType(lua_State *L); +extern "C" int luaopen_compresseddata(lua_State *L); + +} // image +} // love + +#endif // LOVE_IMAGE_WRAP_COMRESSED_DATA_H diff --git a/src/modules/image/wrap_Image.cpp b/src/modules/image/wrap_Image.cpp index a730fe9f4..d77079caf 100644 --- a/src/modules/image/wrap_Image.cpp +++ b/src/modules/image/wrap_Image.cpp @@ -93,16 +93,98 @@ int w_newImageData(lua_State *L) return 1; } +int w_newCompressedData(lua_State *L) +{ + // Case 1: Data + if (luax_istype(L, 1, DATA_T)) + { + Data *d = luax_checktype(L, 1, "Data", DATA_T); + + CompressedData *t = 0; + try + { + t = instance->newCompressedData(d); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } + luax_newtype(L, "CompressedData", IMAGE_COMPRESSED_DATA_T, (void *) t); + + return 1; + } + + // Case 2: String/File. + + // Convert to File, if necessary. + if (lua_isstring(L, 1)) + luax_convobj(L, 1, "filesystem", "newFile"); + + love::filesystem::File *file = luax_checktype(L, 1, "File", FILESYSTEM_FILE_T); + + CompressedData *t = 0; + try + { + t = instance->newCompressedData(file); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } + luax_newtype(L, "CompressedData", IMAGE_COMPRESSED_DATA_T, (void *) t); + + return 1; +} + +int w_isCompressed(lua_State *L) +{ + if (luax_istype(L, 1, DATA_T)) + { + Data *d = luax_checktype(L, 1, "Data", DATA_T); + try + { + bool compressed = instance->isCompressed(d); + luax_pushboolean(L, compressed); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } + return 1; + } + + // Convert to File, if necessary. + if (lua_isstring(L, 1)) + luax_convobj(L, 1, "filesystem", "newFile"); + + filesystem::File *file = luax_checktype(L, 1, "File", FILESYSTEM_FILE_T); + + try + { + bool compressed = instance->isCompressed(file); + luax_pushboolean(L, compressed); + } + catch (love::Exception &e) + { + return luaL_error(L, "%s", e.what()); + } + + return 1; +} + // List of functions to wrap. static const luaL_Reg functions[] = { { "newImageData", w_newImageData }, + { "newCompressedData", w_newCompressedData }, + { "isCompressed", w_isCompressed }, { 0, 0 } }; static const lua_CFunction types[] = { luaopen_imagedata, + luaopen_compresseddata, 0 }; diff --git a/src/modules/image/wrap_Image.h b/src/modules/image/wrap_Image.h index b58d551ae..57bdc9957 100644 --- a/src/modules/image/wrap_Image.h +++ b/src/modules/image/wrap_Image.h @@ -24,14 +24,16 @@ // LOVE #include "Image.h" #include "wrap_ImageData.h" +#include "wrap_CompressedData.h" namespace love { namespace image { -int w_getFormats(lua_State *L); int w_newImageData(lua_State *L); +int w_newCompressedData(lua_State *L); +int w_isCompressed(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_image(lua_State *L); } // image