Browse Source

Added support for DXT1/3/5 and BC5/7 compressed textures via love.image.CompressedData. Some internals are still iffy.
- added a new CompressedData type to love.image
- added love.image.isCompressed(file or data)
- love.graphics.newImage automatically tests for compression when loading a file
- added love.graphics.isSupported strings for DXT, BC5, and BC7

Compressed textures meant to be used on the GPU offer huge performance gains when
- loading the texture from a file
- loading the texture from RAM to VRAM
- loading mipmaps
- rendering

--HG--
branch : image-CompressedData

Alex Szpakowski 12 years ago
parent
commit
5301026f3c

+ 45 - 1
platform/macosx/love-framework.xcodeproj/project.pbxproj

@@ -225,7 +225,16 @@
 		FA577ACD16C7514C00860150 /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7716C71A0800860150 /* Vorbis.framework */; };
 		FA577ACD16C7514C00860150 /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA577A7716C71A0800860150 /* Vorbis.framework */; };
 		FA7C937A16DCC6C2006F2BEE /* wrap_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */; };
 		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 */; };
 		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 */; };
 		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 */; };
 		FAF272A416E3D44400CC193A /* Channel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF2729816E3D44400CC193A /* Channel.cpp */; };
 		FAF272A516E3D44400CC193A /* Channel.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF2729916E3D44400CC193A /* Channel.h */; };
 		FAF272A516E3D44400CC193A /* Channel.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF2729916E3D44400CC193A /* Channel.h */; };
 		FAF272A616E3D44400CC193A /* LuaThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF2729A16E3D44400CC193A /* LuaThread.cpp */; };
 		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; };
 		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 = "<group>"; };
 		FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Math.cpp; sourceTree = "<group>"; };
 		FA7C937616DCC6C2006F2BEE /* wrap_Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Math.h; sourceTree = "<group>"; };
 		FA7C937616DCC6C2006F2BEE /* wrap_Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Math.h; sourceTree = "<group>"; };
+		FAAC6B00170A373A008A61C5 /* CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompressedData.cpp; sourceTree = "<group>"; };
+		FAAC6B01170A373A008A61C5 /* CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressedData.h; sourceTree = "<group>"; };
 		FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "OpenAL-Soft.framework"; path = "/Library/Frameworks/OpenAL-Soft.framework"; sourceTree = "<absolute>"; };
 		FAAFF04316CB11C700CCDE45 /* OpenAL-Soft.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "OpenAL-Soft.framework"; path = "/Library/Frameworks/OpenAL-Soft.framework"; sourceTree = "<absolute>"; };
+		FAE010D8170DDE99006F29D0 /* ddsinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsinfo.h; sourceTree = "<group>"; };
+		FAE010D9170DDE99006F29D0 /* ddsparse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ddsparse.cpp; sourceTree = "<group>"; };
+		FAE010DA170DDE99006F29D0 /* ddsparse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsparse.h; sourceTree = "<group>"; };
+		FAE010DE170DE25E006F29D0 /* CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CompressedData.cpp; sourceTree = "<group>"; };
+		FAE010DF170DE25E006F29D0 /* CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressedData.h; sourceTree = "<group>"; };
+		FAE010E2170DF75B006F29D0 /* wrap_CompressedData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_CompressedData.cpp; sourceTree = "<group>"; };
+		FAE010E3170DF75C006F29D0 /* wrap_CompressedData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_CompressedData.h; sourceTree = "<group>"; };
 		FAF2729816E3D44400CC193A /* Channel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Channel.cpp; sourceTree = "<group>"; };
 		FAF2729816E3D44400CC193A /* Channel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Channel.cpp; sourceTree = "<group>"; };
 		FAF2729916E3D44400CC193A /* Channel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Channel.h; sourceTree = "<group>"; };
 		FAF2729916E3D44400CC193A /* Channel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Channel.h; sourceTree = "<group>"; };
 		FAF2729A16E3D44400CC193A /* LuaThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LuaThread.cpp; sourceTree = "<group>"; };
 		FAF2729A16E3D44400CC193A /* LuaThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LuaThread.cpp; sourceTree = "<group>"; };
@@ -748,14 +766,18 @@
 		003F4BA82B6046BC133B3F0F /* image */ = {
 		003F4BA82B6046BC133B3F0F /* image */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				FAAC6B00170A373A008A61C5 /* CompressedData.cpp */,
+				FAAC6B01170A373A008A61C5 /* CompressedData.h */,
+				13730AB030E309FF6E2961F1 /* devil */,
 				25C325DC2128769F6C6A54C3 /* Image.h */,
 				25C325DC2128769F6C6A54C3 /* Image.h */,
 				78115E763B723C0C40AD47CF /* ImageData.cpp */,
 				78115E763B723C0C40AD47CF /* ImageData.cpp */,
 				07B301984BE42246402F7D27 /* ImageData.h */,
 				07B301984BE42246402F7D27 /* ImageData.h */,
-				13730AB030E309FF6E2961F1 /* devil */,
 				0B0728FA73B107B37A956A09 /* wrap_Image.cpp */,
 				0B0728FA73B107B37A956A09 /* wrap_Image.cpp */,
 				006B015320155B4D42B43B61 /* wrap_Image.h */,
 				006B015320155B4D42B43B61 /* wrap_Image.h */,
 				076840774B0B6E721D0C18D0 /* wrap_ImageData.cpp */,
 				076840774B0B6E721D0C18D0 /* wrap_ImageData.cpp */,
 				31B85B507F466FE158A3718E /* wrap_ImageData.h */,
 				31B85B507F466FE158A3718E /* wrap_ImageData.h */,
+				FAE010E2170DF75B006F29D0 /* wrap_CompressedData.cpp */,
+				FAE010E3170DF75C006F29D0 /* wrap_CompressedData.h */,
 			);
 			);
 			path = image;
 			path = image;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -912,6 +934,8 @@
 				68616BD516DB124312B47EB3 /* Image.h */,
 				68616BD516DB124312B47EB3 /* Image.h */,
 				1AA7781A230065F346E2313A /* ImageData.cpp */,
 				1AA7781A230065F346E2313A /* ImageData.cpp */,
 				283342E174613897621A43F1 /* ImageData.h */,
 				283342E174613897621A43F1 /* ImageData.h */,
+				FAE010DE170DE25E006F29D0 /* CompressedData.cpp */,
+				FAE010DF170DE25E006F29D0 /* CompressedData.h */,
 			);
 			);
 			path = devil;
 			path = devil;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -1194,6 +1218,7 @@
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				63287ED84D0F2EEB65D249A3 /* Box2D */,
 				63287ED84D0F2EEB65D249A3 /* Box2D */,
+				FAE010D7170DDE99006F29D0 /* ddsparse */,
 				3AED1DE005A53DDB07902760 /* luasocket */,
 				3AED1DE005A53DDB07902760 /* luasocket */,
 				36C14C81334735EC54E33637 /* utf8 */,
 				36C14C81334735EC54E33637 /* utf8 */,
 			);
 			);
@@ -1619,6 +1644,16 @@
 			path = math;
 			path = math;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		FAE010D7170DDE99006F29D0 /* ddsparse */ = {
+			isa = PBXGroup;
+			children = (
+				FAE010D8170DDE99006F29D0 /* ddsinfo.h */,
+				FAE010D9170DDE99006F29D0 /* ddsparse.cpp */,
+				FAE010DA170DDE99006F29D0 /* ddsparse.h */,
+			);
+			path = ddsparse;
+			sourceTree = "<group>";
+		};
 		FAF272B016E3D46400CC193A /* sdl */ = {
 		FAF272B016E3D46400CC193A /* sdl */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -1647,6 +1682,11 @@
 				FAF272B616E3D46400CC193A /* Thread.h in Headers */,
 				FAF272B616E3D46400CC193A /* Thread.h in Headers */,
 				FAF272B816E3D46400CC193A /* threads.h in Headers */,
 				FAF272B816E3D46400CC193A /* threads.h in Headers */,
 				FA5454C316F1310000D30303 /* MathModule.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;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -1922,6 +1962,10 @@
 				FAF272B516E3D46400CC193A /* Thread.cpp in Sources */,
 				FAF272B516E3D46400CC193A /* Thread.cpp in Sources */,
 				FAF272B716E3D46400CC193A /* threads.cpp in Sources */,
 				FAF272B716E3D46400CC193A /* threads.cpp in Sources */,
 				FA5454C216F1310000D30303 /* MathModule.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;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};

+ 1 - 0
src/common/runtime.cpp

@@ -469,6 +469,7 @@ StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[] =
 
 
 	// Image
 	// Image
 	{"ImageData", IMAGE_IMAGE_DATA_ID},
 	{"ImageData", IMAGE_IMAGE_DATA_ID},
+	{"CompressedData", IMAGE_COMPRESSED_DATA_ID},
 
 
 	// Audio
 	// Audio
 	{"Source", AUDIO_SOURCE_ID},
 	{"Source", AUDIO_SOURCE_ID},

+ 2 - 0
src/common/types.h

@@ -56,6 +56,7 @@ enum Type
 
 
 	// Image
 	// Image
 	IMAGE_IMAGE_DATA_ID,
 	IMAGE_IMAGE_DATA_ID,
+	IMAGE_COMPRESSED_DATA_ID,
 
 
 	// Audio
 	// Audio
 	AUDIO_SOURCE_ID,
 	AUDIO_SOURCE_ID,
@@ -127,6 +128,7 @@ const bits GRAPHICS_SHADER_T = (bits(1) << GRAPHICS_SHADER_ID) | OBJECT_T;
 
 
 // Image.
 // Image.
 const bits IMAGE_IMAGE_DATA_T = (bits(1) << IMAGE_IMAGE_DATA_ID) | DATA_T;
 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.
 // Audio.
 const bits AUDIO_SOURCE_T = (bits(1) << AUDIO_SOURCE_ID) | OBJECT_T;
 const bits AUDIO_SOURCE_T = (bits(1) << AUDIO_SOURCE_ID) | OBJECT_T;

+ 235 - 0
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 <stdint.h>
+
+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

+ 368 - 0
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 <algorithm>
+
+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<Image>::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<Image>::iterator it;
+		for (it = texData.begin(); it != texData.end(); ++it)
+		{
+			delete[] it->data;
+			it->data = 0;
+		}
+	}
+
+	texData.clear();
+}
+
+} // dds

+ 139 - 0
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 <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+#include <exception>
+
+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<Image> texData;
+	Format format;
+	Options options;
+
+}; // Parser
+
+} // dds
+
+#endif // DDS_PARSE_H

+ 3 - 0
src/modules/graphics/Graphics.cpp

@@ -163,6 +163,9 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
 	{ "npot", Graphics::SUPPORT_NPOT },
 	{ "npot", Graphics::SUPPORT_NPOT },
 	{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
 	{ "subtractive", Graphics::SUPPORT_SUBTRACTIVE },
 	{ "mipmap", Graphics::SUPPORT_MIPMAP },
 	{ "mipmap", Graphics::SUPPORT_MIPMAP },
+	{ "dxt", Graphics::SUPPORT_DXT },
+	{ "bc5", Graphics::SUPPORT_BC5 },
+	{ "bc7", Graphics::SUPPORT_BC7 },
 };
 };
 
 
 StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
 StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));

+ 3 - 0
src/modules/graphics/Graphics.h

@@ -92,6 +92,9 @@ public:
 		SUPPORT_NPOT,
 		SUPPORT_NPOT,
 		SUPPORT_SUBTRACTIVE,
 		SUPPORT_SUBTRACTIVE,
 		SUPPORT_MIPMAP,
 		SUPPORT_MIPMAP,
+		SUPPORT_DXT,
+		SUPPORT_BC5,
+		SUPPORT_BC7,
 		SUPPORT_MAX_ENUM
 		SUPPORT_MAX_ENUM
 	};
 	};
 
 

+ 23 - 0
src/modules/graphics/opengl/Graphics.cpp

@@ -373,6 +373,29 @@ Image *Graphics::newImage(love::image::ImageData *data)
 	return image;
 	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 *Graphics::newQuad(float x, float y, float w, float h, float sw, float sh)
 {
 {
 	Quad::Viewport v;
 	Quad::Viewport v;

+ 1 - 1
src/modules/graphics/opengl/Graphics.h

@@ -251,8 +251,8 @@ public:
 	/**
 	/**
 	 * Creates an Image object with padding and/or optimization.
 	 * 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::ImageData *data);
+	Image *newImage(love::image::CompressedData *cdata);
 
 
 	/**
 	/**
 	 * Creates a Quad object.
 	 * Creates a Quad object.

+ 210 - 55
src/modules/graphics/opengl/Image.cpp

@@ -39,43 +39,39 @@ Image::FilterMode Image::defaultMipmapFilter = Image::FILTER_NONE;
 float Image::defaultMipmapSharpness = 0.0f;
 float Image::defaultMipmapSharpness = 0.0f;
 
 
 Image::Image(love::image::ImageData *data)
 Image::Image(love::image::ImageData *data)
-	: width((float)(data->getWidth()))
+	: cdata(0)
+	, width((float)(data->getWidth()))
 	, height((float)(data->getHeight()))
 	, height((float)(data->getHeight()))
 	, texture(0)
 	, texture(0)
 	, mipmapSharpness(defaultMipmapSharpness)
 	, mipmapSharpness(defaultMipmapSharpness)
 	, mipmapsCreated(false)
 	, mipmapsCreated(false)
+	, isCompressed(false)
 {
 {
 	data->retain();
 	data->retain();
 	this->data = data;
 	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()
 Image::~Image()
 {
 {
 	if (data != 0)
 	if (data != 0)
 		data->release();
 		data->release();
+	if (cdata != 0)
+		cdata->release();
 	unload();
 	unload();
 }
 }
 
 
@@ -155,18 +151,46 @@ void Image::checkMipmapsCreated()
 	if (mipmapsCreated || (filter.mipmap != FILTER_NEAREST && filter.mipmap != FILTER_LINEAR))
 	if (mipmapsCreated || (filter.mipmap != FILTER_NEAREST && filter.mipmap != FILTER_LINEAR))
 		return;
 		return;
 
 
-	if (!hasMipmapSupport())
+	if (!(isCompressed && cdata) && !hasMipmapSupport())
 		throw love::Exception("Mipmap filtering is not supported on this system.");
 		throw love::Exception("Mipmap filtering is not supported on this system.");
 
 
 	// Some old drivers claim support for NPOT textures, but fail when creating mipmaps.
 	// 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.
 	// we can't detect which systems will do this, so we fail gracefully for all NPOT images.
 	int w = int(width), h = int(height);
 	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.");
 		throw love::Exception("Cannot create mipmaps: image does not have power of two dimensions.");
 
 
 	bind();
 	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,
 		// AMD/ATI drivers have several bugs when generating mipmaps,
 		// re-uploading the entire base image seems to be required.
 		// re-uploading the entire base image seems to be required.
@@ -184,7 +208,7 @@ void Image::checkMipmapsCreated()
 		glEnable(GL_TEXTURE_2D);
 		glEnable(GL_TEXTURE_2D);
 		glGenerateMipmap(GL_TEXTURE_2D);
 		glGenerateMipmap(GL_TEXTURE_2D);
 	}
 	}
-	else
+	else if (data)
 	{
 	{
 		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
 		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
 		glTexSubImage2D(GL_TEXTURE_2D,
 		glTexSubImage2D(GL_TEXTURE_2D,
@@ -197,6 +221,8 @@ void Image::checkMipmapsCreated()
 		                GL_UNSIGNED_BYTE,
 		                GL_UNSIGNED_BYTE,
 		                data->getData());
 		                data->getData());
 	}
 	}
+	else
+		return;
 
 
 	mipmapsCreated = true;
 	mipmapsCreated = true;
 }
 }
@@ -255,6 +281,32 @@ void Image::bind() const
 	bindTexture(texture);
 	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()
 bool Image::load()
 {
 {
 	return loadVolatile();
 	return loadVolatile();
@@ -267,6 +319,18 @@ void Image::unload()
 
 
 bool Image::loadVolatile()
 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)
 	if (hasMipmapSharpnessSupport() && maxMipmapSharpness == 0.0f)
 		glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
 		glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
 
 
@@ -296,25 +360,47 @@ bool Image::loadVolatilePOT()
 
 
 	while (glGetError() != GL_NO_ERROR); // clear errors
 	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);
-
-	glTexSubImage2D(GL_TEXTURE_2D,
-	                0,
-	                0,
-	                0,
-	                (GLsizei)width,
-	                (GLsizei)height,
-	                GL_RGBA,
-	                GL_UNSIGNED_BYTE,
-	                data->getData());
+	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.");
+		}
+
+		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)
 	if (glGetError() != GL_NO_ERROR)
 		throw love::Exception("Cannot create image: size may be too large for this system.");
 		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
 	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)
 	if (glGetError() != GL_NO_ERROR)
 		throw love::Exception("Cannot create image: size may be too large for this system.");
 		throw love::Exception("Cannot create image: size may be too large for this system.");
@@ -407,6 +508,29 @@ Image::FilterMode Image::getDefaultMipmapFilter()
 	return defaultMipmapFilter;
 	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()
 bool Image::hasNpot()
 {
 {
 	return GLEE_VERSION_2_0 || GLEE_ARB_texture_non_power_of_two;
 	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;
 	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
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

+ 21 - 1
src/modules/graphics/opengl/Image.h

@@ -26,6 +26,7 @@
 #include "common/math.h"
 #include "common/math.h"
 #include "common/config.h"
 #include "common/config.h"
 #include "image/ImageData.h"
 #include "image/ImageData.h"
+#include "image/CompressedData.h"
 #include "graphics/Image.h"
 #include "graphics/Image.h"
 
 
 // OpenGL
 // OpenGL
@@ -52,10 +53,17 @@ public:
 	 * Creates a new Image. Not that anything is ready to use
 	 * Creates a new Image. Not that anything is ready to use
 	 * before load is called.
 	 * 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);
 	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.
 	 * Destructor. Deletes the hardware texture and other resources.
 	 **/
 	 **/
@@ -126,6 +134,9 @@ public:
 	static bool hasMipmapSupport();
 	static bool hasMipmapSupport();
 	static bool hasMipmapSharpnessSupport();
 	static bool hasMipmapSharpnessSupport();
 
 
+	static bool hasCompressedTextureSupport();
+	static bool hasCompressedTextureSupport(image::CompressedData::TextureType type);
+
 private:
 private:
 
 
 	void drawv(const Matrix &t, const vertex *v) const;
 	void drawv(const Matrix &t, const vertex *v) const;
@@ -138,6 +149,9 @@ private:
 	// The ImageData from which the texture is created.
 	// The ImageData from which the texture is created.
 	love::image::ImageData *data;
 	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.
 	// Width and height of the hardware texture.
 	float width, height;
 	float width, height;
 
 
@@ -153,12 +167,16 @@ private:
 	// True if mipmaps have been created for this Image.
 	// True if mipmaps have been created for this Image.
 	bool mipmapsCreated;
 	bool mipmapsCreated;
 
 
+	bool isCompressed;
+
 	// The image's filter mode
 	// The image's filter mode
 	Image::Filter filter;
 	Image::Filter filter;
 
 
 	// The image's wrap mode
 	// The image's wrap mode
 	Image::Wrap wrap;
 	Image::Wrap wrap;
 
 
+	void preload();
+
 	bool loadVolatilePOT();
 	bool loadVolatilePOT();
 	bool loadVolatileNPOT();
 	bool loadVolatileNPOT();
 
 
@@ -169,6 +187,8 @@ private:
 	static FilterMode defaultMipmapFilter;
 	static FilterMode defaultMipmapFilter;
 	static float defaultMipmapSharpness;
 	static float defaultMipmapSharpness;
 
 
+	GLenum getCompressedFormat(image::CompressedData::TextureType type) const;
+
 }; // Image
 }; // Image
 
 
 } // opengl
 } // opengl

+ 8 - 0
src/modules/graphics/opengl/OpenGL.cpp

@@ -97,6 +97,14 @@ void initializeContext()
 		glUnmapBufferARB = (GLEEPFNGLUNMAPBUFFERARBPROC) glUnmapBuffer;
 		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)
 	if (GLEE_EXT_texture_filter_anisotropic)
 		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
 		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
 	else
 	else

+ 40 - 4
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -269,21 +269,45 @@ int w_setInvertedStencil(lua_State *L)
 
 
 int w_newImage(lua_State *L)
 int w_newImage(lua_State *L)
 {
 {
+	love::image::ImageData *data = 0;
+	love::image::CompressedData *cdata = 0;
+
 	// Convert to File, if necessary.
 	// Convert to File, if necessary.
 	if (lua_isstring(L, 1))
 	if (lua_isstring(L, 1))
 		luax_convobj(L, 1, "filesystem", "newFile");
 		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))
 	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<love::image::ImageData>(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<love::image::CompressedData>(L, 1, "CompressedData", IMAGE_COMPRESSED_DATA_T);
+	else
+		data = luax_checktype<love::image::ImageData>(L, 1, "ImageData", IMAGE_IMAGE_DATA_T);
 
 
 	// Create the image.
 	// Create the image.
 	Image *image = 0;
 	Image *image = 0;
 	try
 	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)
 	catch(love::Exception &e)
 	{
 	{
@@ -1110,6 +1134,18 @@ int w_isSupported(lua_State *L)
 			if (!Image::hasMipmapSupport())
 			if (!Image::hasMipmapSupport())
 				supported = false;
 				supported = false;
 			break;
 			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:
 		default:
 			supported = false;
 			supported = false;
 		}
 		}

+ 123 - 0
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<CompressedData::TextureType, CompressedData::TYPE_MAX_ENUM>::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::TextureType, CompressedData::TYPE_MAX_ENUM> CompressedData::types(CompressedData::typeEntries, sizeof(CompressedData::typeEntries));
+
+} // image
+} // love

+ 125 - 0
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 <vector>
+
+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<unsigned char> data;
+	};
+
+	TextureType type;
+
+	// Texture info for each mipmap level.
+	std::vector<MipmapInfo> dataMipmapInfo;
+
+	void checkMipmapLevelExists(int miplevel) const;
+
+private:
+
+	static StringMap<TextureType, TYPE_MAX_ENUM>::Entry typeEntries[];
+	static StringMap<TextureType, TYPE_MAX_ENUM> types;
+
+}; // CompressedData
+
+} // image
+} // love
+
+#endif // LOVE_IMAGE_COMPRESSED_DATA_H

+ 21 - 0
src/modules/image/Image.h

@@ -26,6 +26,7 @@
 #include "common/Module.h"
 #include "common/Module.h"
 #include "filesystem/File.h"
 #include "filesystem/File.h"
 #include "ImageData.h"
 #include "ImageData.h"
+#include "CompressedData.h"
 
 
 namespace love
 namespace love
 {
 {
@@ -77,6 +78,26 @@ public:
 	 **/
 	 **/
 	virtual ImageData *newImageData(int width, int height, void *data) = 0;
 	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
 
 
 } // image
 } // image

+ 7 - 7
src/modules/image/ImageData.cpp

@@ -39,14 +39,19 @@ ImageData::~ImageData()
 	delete mutex;
 	delete mutex;
 }
 }
 
 
+int ImageData::getSize() const
+{
+	return getWidth()*getHeight()*sizeof(pixel);
+}
+
 void *ImageData::getData() const
 void *ImageData::getData() const
 {
 {
 	return data;
 	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
 int ImageData::getWidth() const
@@ -59,11 +64,6 @@ int ImageData::getHeight() const
 	return height;
 	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)
 void ImageData::setPixel(int x, int y, pixel c)
 {
 {
 	if (!inside(x, y))
 	if (!inside(x, y))

+ 19 - 18
src/modules/image/ImageData.h

@@ -45,22 +45,6 @@ struct pixel
  **/
  **/
 class ImageData : public Data
 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:
 public:
 
 
 	enum Format
 	enum Format
@@ -137,10 +121,27 @@ public:
 	virtual void encode(love::filesystem::File *f, Format format) = 0;
 	virtual void encode(love::filesystem::File *f, Format format) = 0;
 
 
 	// Implements Data.
 	// 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:
 private:
+
 	static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
 	static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
 	static StringMap<Format, FORMAT_MAX_ENUM> formats;
 	static StringMap<Format, FORMAT_MAX_ENUM> formats;
 
 

+ 133 - 0
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

+ 60 - 0
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

+ 47 - 0
src/modules/image/devil/Image.cpp

@@ -21,6 +21,7 @@
 #include "Image.h"
 #include "Image.h"
 
 
 #include "ImageData.h"
 #include "ImageData.h"
+#include "CompressedData.h"
 
 
 // DevIL
 // DevIL
 #include <IL/il.h>
 #include <IL/il.h>
@@ -32,6 +33,11 @@ namespace image
 namespace devil
 namespace devil
 {
 {
 
 
+const std::string Image::compressedExts[] =
+{
+	".dds", ""
+};
+
 Image::Image()
 Image::Image()
 {
 {
 	ilInit();
 	ilInit();
@@ -69,6 +75,47 @@ love::image::ImageData *Image::newImageData(int width, int height, void *data)
 	return new ImageData(width, height, 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
 } // devil
 } // image
 } // image
 } // love
 } // love

+ 11 - 0
src/modules/image/devil/Image.h

@@ -24,6 +24,9 @@
 // LOVE
 // LOVE
 #include "image/Image.h"
 #include "image/Image.h"
 
 
+// STL
+#include <string>
+
 namespace love
 namespace love
 {
 {
 namespace image
 namespace image
@@ -46,6 +49,14 @@ public:
 	love::image::ImageData *newImageData(int width, int height);
 	love::image::ImageData *newImageData(int width, int height);
 	love::image::ImageData *newImageData(int width, int height, void *data);
 	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
 }; // Image
 
 
 } // devil
 } // devil

+ 129 - 0
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<CompressedData>(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

+ 44 - 0
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

+ 82 - 0
src/modules/image/wrap_Image.cpp

@@ -93,16 +93,98 @@ int w_newImageData(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
+int w_newCompressedData(lua_State *L)
+{
+	// Case 1: Data
+	if (luax_istype(L, 1, DATA_T))
+	{
+		Data *d = luax_checktype<Data>(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<love::filesystem::File>(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<Data>(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<filesystem::File>(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.
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
 	{ "newImageData",  w_newImageData },
 	{ "newImageData",  w_newImageData },
+	{ "newCompressedData", w_newCompressedData },
+	{ "isCompressed", w_isCompressed },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 
 static const lua_CFunction types[] =
 static const lua_CFunction types[] =
 {
 {
 	luaopen_imagedata,
 	luaopen_imagedata,
+	luaopen_compresseddata,
 	0
 	0
 };
 };
 
 

+ 3 - 1
src/modules/image/wrap_Image.h

@@ -24,14 +24,16 @@
 // LOVE
 // LOVE
 #include "Image.h"
 #include "Image.h"
 #include "wrap_ImageData.h"
 #include "wrap_ImageData.h"
+#include "wrap_CompressedData.h"
 
 
 namespace love
 namespace love
 {
 {
 namespace image
 namespace image
 {
 {
 
 
-int w_getFormats(lua_State *L);
 int w_newImageData(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);
 extern "C" LOVE_EXPORT int luaopen_love_image(lua_State *L);
 
 
 } // image
 } // image