Browse Source

Added a new backend for loading and saving images using Apple's ImageIO framework. It's not enabled on any platform yet.

--HG--
branch : minor
Alex Szpakowski 10 years ago
parent
commit
482b61187f

+ 8 - 0
platform/macosx/love-framework.xcodeproj/project.pbxproj

@@ -11,6 +11,8 @@
 		FA01BE1F1878E35B00640047 /* wrap_Texture.h in Headers */ = {isa = PBXBuildFile; fileRef = FA01BE1D1878E35B00640047 /* wrap_Texture.h */; };
 		FA03546C1731F3A700284828 /* simplexnoise1234.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA03546A1731F3A700284828 /* simplexnoise1234.cpp */; };
 		FA03546D1731F3A700284828 /* simplexnoise1234.h in Headers */ = {isa = PBXBuildFile; fileRef = FA03546B1731F3A700284828 /* simplexnoise1234.h */; };
+		FA0706E11A93ED2800AF268C /* ImageIOHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0706DF1A93ED2800AF268C /* ImageIOHandler.cpp */; };
+		FA0706E21A93ED2800AF268C /* ImageIOHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0706E01A93ED2800AF268C /* ImageIOHandler.h */; };
 		FA088D861A919E1400F26F29 /* OSX.h in Headers */ = {isa = PBXBuildFile; fileRef = FA088D841A919E1400F26F29 /* OSX.h */; };
 		FA088D871A919E1400F26F29 /* OSX.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA088D851A919E1400F26F29 /* OSX.mm */; };
 		FA08F5B016C752F900F007B5 /* Source.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2FB732687B1669402408356D /* Source.cpp */; };
@@ -795,6 +797,8 @@
 		FA01BE1D1878E35B00640047 /* wrap_Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Texture.h; sourceTree = "<group>"; };
 		FA03546A1731F3A700284828 /* simplexnoise1234.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simplexnoise1234.cpp; sourceTree = "<group>"; };
 		FA03546B1731F3A700284828 /* simplexnoise1234.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simplexnoise1234.h; sourceTree = "<group>"; };
+		FA0706DF1A93ED2800AF268C /* ImageIOHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageIOHandler.cpp; sourceTree = "<group>"; };
+		FA0706E01A93ED2800AF268C /* ImageIOHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageIOHandler.h; sourceTree = "<group>"; };
 		FA088D841A919E1400F26F29 /* OSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX.h; sourceTree = "<group>"; };
 		FA088D851A919E1400F26F29 /* OSX.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSX.mm; sourceTree = "<group>"; };
 		FA08F5AE16C7525600F007B5 /* Info-Framework.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-Framework.plist"; sourceTree = "<group>"; };
@@ -1183,6 +1187,8 @@
 				68616BD516DB124312B47EB3 /* Image.h */,
 				FAEC80881710FEA60057279A /* ImageData.cpp */,
 				FAEC80891710FEA60057279A /* ImageData.h */,
+				FA0706DF1A93ED2800AF268C /* ImageIOHandler.cpp */,
+				FA0706E01A93ED2800AF268C /* ImageIOHandler.h */,
 				FA48E43418F10D00007CF0BD /* JPEGHandler.cpp */,
 				FA48E43518F10D00007CF0BD /* JPEGHandler.h */,
 				FA6F55701A6E261800062204 /* KTXHandler.cpp */,
@@ -2176,6 +2182,7 @@
 				FAEC808F1711E76C0057279A /* CompressedData.h in Headers */,
 				FA0F0D5B19A2CFEB0010A75B /* glad.hpp in Headers */,
 				FA636D8B171B70920065623F /* RandomGenerator.h in Headers */,
+				FA0706E21A93ED2800AF268C /* ImageIOHandler.h in Headers */,
 				FA6F557B1A6E261800062204 /* PVRHandler.h in Headers */,
 				FA636D8F171B72A70065623F /* wrap_RandomGenerator.h in Headers */,
 				FAC86E641724552C00EED715 /* wrap_Quad.h in Headers */,
@@ -2527,6 +2534,7 @@
 				FAC86E6B1724555D00EED715 /* Quad.cpp in Sources */,
 				FA03546C1731F3A700284828 /* simplexnoise1234.cpp in Sources */,
 				FA3D9E0D16E68DE600CA6630 /* Cursor.cpp in Sources */,
+				FA0706E11A93ED2800AF268C /* ImageIOHandler.cpp in Sources */,
 				FA3D9E1116E68EAE00CA6630 /* Cursor.cpp in Sources */,
 				FA3D9E1516E6D41100CA6630 /* wrap_Cursor.cpp in Sources */,
 				FA9FC0B0173D6E3E005027FF /* wrap_Window.cpp in Sources */,

+ 5 - 0
src/modules/image/magpie/Image.cpp

@@ -26,6 +26,7 @@
 #include "JPEGHandler.h"
 #include "PNGHandler.h"
 #include "STBHandler.h"
+#include "ImageIOHandler.h"
 
 #include "ddsHandler.h"
 #include "PVRHandler.h"
@@ -45,6 +46,10 @@ Image::Image()
 	formatHandlers.push_back(new JPEGHandler);
 	formatHandlers.push_back(new STBHandler);
 
+#ifdef LOVE_SUPPORT_IMAGEIO
+	formatHandlers.push_back(new ImageIOHandler);
+#endif
+
 	compressedFormatHandlers.push_back(new DDSHandler);
 	compressedFormatHandlers.push_back(new PVRHandler);
 	compressedFormatHandlers.push_back(new KTXHandler);

+ 280 - 0
src/modules/image/magpie/ImageIOHandler.cpp

@@ -0,0 +1,280 @@
+/**
+ * Copyright (c) 2006-2015 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "ImageIOHandler.h"
+
+#ifdef LOVE_SUPPORT_IMAGEIO
+
+// LOVE
+#include "common/Exception.h"
+#include "common/math.h"
+#include "filesystem/File.h"
+
+// ImageIO
+#include <ImageIO/ImageIO.h>
+
+#if defined(LOVE_IOS)
+#include <MobileCoreServices/MobileCoreServices.h>
+#elif defined(LOVE_MACOSX)
+#include <CoreServices/CoreServices.h>
+#endif
+
+// STL
+#include <cstring>
+#include <iostream>
+
+using love::thread::Lock;
+
+namespace love
+{
+namespace image
+{
+namespace magpie
+{
+
+static const CFStringRef invalidFormat = CFSTR("");
+
+static CFStringRef getFormatType(ImageData::Format format)
+{
+	switch (format)
+	{
+	case ImageData::FORMAT_BMP:
+		return kUTTypeBMP;
+	case ImageData::FORMAT_JPG:
+		return kUTTypeJPEG;
+	case ImageData::FORMAT_TGA:
+		return CFSTR("com.truevision.tga-image");
+	case ImageData::FORMAT_PNG:
+		return kUTTypePNG;
+	default:
+		return invalidFormat;
+	}
+}
+
+ImageIOHandler::ImageIOHandler()
+	: mutex(nullptr)
+{
+	mutex = love::thread::newMutex();
+}
+
+ImageIOHandler::~ImageIOHandler()
+{
+	delete mutex;
+}
+
+bool ImageIOHandler::canDecode(love::filesystem::FileData *data)
+{
+	CFStringRef cfext = CFStringCreateWithCString(nullptr, data->getExtension().c_str(), kCFStringEncodingUTF8);
+	CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, cfext, nullptr);
+
+	CFRelease(cfext);
+
+	if (!UTI)
+		return false;
+
+	CFArrayRef types = CGImageSourceCopyTypeIdentifiers();
+	Boolean isdecodable = CFArrayContainsValue(types, CFRangeMake(0, CFArrayGetCount(types)), UTI);
+
+	CFRelease(UTI);
+	CFRelease(types);
+
+	return isdecodable;
+}
+
+bool ImageIOHandler::canEncode(ImageData::Format format)
+{
+	CFStringRef cftype = getFormatType(format);
+	if (cftype == invalidFormat)
+		return false;
+
+	// Is the format supported for writing on this system?
+	CFArrayRef types = CGImageDestinationCopyTypeIdentifiers();
+
+	Boolean iswritable = CFArrayContainsValue(types, CFRangeMake(0, CFArrayGetCount(types)), cftype);
+	CFRelease(types);
+
+	return iswritable;
+}
+
+ImageIOHandler::DecodedImage ImageIOHandler::decode(love::filesystem::FileData *data)
+{
+	Lock lock(mutex);
+
+	DecodedImage img;
+
+	CFDataRef cfdata = CFDataCreateWithBytesNoCopy(nullptr, (const UInt8 *) data->getData(), data->getSize(), kCFAllocatorNull);
+	CGImageSourceRef source = CGImageSourceCreateWithData(cfdata, nullptr);
+	CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, nullptr);
+
+	CFRelease(cfdata);
+	CFRelease(source);
+
+	if (!image)
+		throw love::Exception("Could not decode image!");
+
+	img.width = CGImageGetWidth(image);
+	img.height = CGImageGetHeight(image);
+
+	CGImageAlphaInfo alphainfo = CGImageGetAlphaInfo(image);
+
+	size_t bpp = CGImageGetBitsPerPixel(image);
+
+	// If the bpp doesn't match, we need to draw the image onto a new 32 bpp canvas
+	if (bpp != 32)
+	{
+		// Create a new bitmap canvas
+		CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
+		CGContextRef context = CGBitmapContextCreate(nullptr, img.width, img.height, 8, img.width*sizeof(pixel), colorspace, kCGImageAlphaPremultipliedLast);
+		CGColorSpaceRelease(colorspace);
+
+		if (!context)
+		{
+			CGImageRelease(image);
+			throw love::Exception("Could not decode image: error converting to 32 bpp!");
+		}
+
+		// Draw a black background
+		CGContextSetRGBFillColor(context, 0, 0, 0, 1);
+		CGContextFillRect(context, CGRectMake(0, 0, img.width, img.height));
+
+		CGContextDrawImage(context, CGRectMake(0, 0, img.width, img.height), image);
+
+		// Replace the old image with the canvas
+		CGImageRef contextimage = CGBitmapContextCreateImage(context);
+		CGContextRelease(context);
+
+		CGImageRelease(image);
+		image = contextimage;
+
+		if (!image)
+			throw love::Exception("Could not decode image: error converting to 32 bpp!");
+	}
+
+	cfdata = CGDataProviderCopyData(CGImageGetDataProvider(image));
+	CGImageRelease(image);
+
+	img.size = CFDataGetLength(cfdata);
+
+	try
+	{
+		img.data = new unsigned char[img.size];
+	}
+	catch (std::bad_alloc &)
+	{
+		CFRelease(cfdata);
+		throw love::Exception("Out of memory.");
+	}
+
+	CFDataGetBytes(cfdata, CFRangeMake(0, img.size), (UInt8 *) img.data);
+	CFRelease(cfdata);
+
+	// ImageIO might try to be "helpful" and premultiply the image's alpha for us,
+	// now we have to un-premultiply it ourselves. Thanks...
+	if (alphainfo == kCGImageAlphaPremultipliedLast)
+	{
+		pixel *pixels = (pixel *) img.data;
+		for (int i = 0; i < img.width * img.height; i++)
+		{
+			unsigned char alpha = pixels[i].a;
+
+			if (alpha > 0 && alpha < 255)
+			{
+				pixels[i].r = ((short) pixels[i].r * 255) / alpha;
+				pixels[i].g = ((short) pixels[i].g * 255) / alpha;
+				pixels[i].b = ((short) pixels[i].b * 255) / alpha;
+			}
+		}
+	}
+
+	return img;
+}
+
+ImageIOHandler::EncodedImage ImageIOHandler::encode(const DecodedImage &img, ImageData::Format format)
+{
+	Lock lock(mutex);
+
+	EncodedImage encodedimage;
+
+	CFStringRef cftype = getFormatType(format);
+
+	if (cftype == invalidFormat || !canEncode(format))
+		throw love::Exception("Could not encode image: format is not supported on this system.");
+
+	CGDataProviderRef provider = CGDataProviderCreateWithData(nullptr, img.data, img.size, nullptr);
+
+	CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+	CGBitmapInfo bitmapinfo = kCGBitmapByteOrderDefault | kCGImageAlphaLast;
+	CGColorRenderingIntent intent = kCGRenderingIntentDefault;
+	size_t pixsize = sizeof(pixel);
+
+	// Create a CoreGraphics image using the ImageData
+	CGImageRef image = CGImageCreate(img.width, img.height, 8, 8*pixsize, img.width*pixsize, space, bitmapinfo, provider, nullptr, false, intent);
+
+	CGColorSpaceRelease(space);
+	CGDataProviderRelease(provider);
+
+	CFMutableDataRef encodeddata = CFDataCreateMutable(nullptr, 0);
+	if (!encodeddata)
+	{
+		CFRelease(image);
+		throw love::Exception("Could not create image for encoding!");
+	}
+
+	// Set up the encoder, using encodeddata as a chunk of memory to store the final result
+	CGImageDestinationRef dest = CGImageDestinationCreateWithData(encodeddata, cftype, 1, nullptr);
+	CGImageDestinationAddImage(dest, image, nullptr);
+
+	// Encode the CoreGraphics image to the specified format
+	bool success = CGImageDestinationFinalize(dest);
+
+	CFRelease(image);
+	CFRelease(dest);
+
+	CFIndex encodedsize = CFDataGetLength(encodeddata);
+
+	if (!success || encodedsize <= 0)
+	{
+		CFRelease(encodeddata);
+		throw love::Exception("Could not encode image!");
+	}
+
+	encodedimage.size = encodedsize;
+
+	try
+	{
+		encodedimage.data = new unsigned char[encodedsize];
+	}
+	catch (std::bad_alloc &)
+	{
+		CFRelease(encodeddata);
+		throw love::Exception("Out of memory");
+	}
+
+	CFDataGetBytes(encodeddata, CFRangeMake(0, encodedsize), encodedimage.data);
+	CFRelease(encodeddata);
+
+	return encodedimage;
+}
+
+} // magpie
+} // image
+} // love
+
+#endif // LOVE_SUPPORT_IMAGEIO

+ 65 - 0
src/modules/image/magpie/ImageIOHandler.h

@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2006-2015 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_IMAGE_MAGPIE_IMAGEIO_HANDLER_H
+#define LOVE_IMAGE_MAGPIE_IMAGEIO_HANDLER_H
+
+#include "common/config.h"
+
+#ifdef LOVE_SUPPORT_IMAGEIO
+
+// LOVE
+#include "FormatHandler.h"
+
+namespace love
+{
+namespace image
+{
+namespace magpie
+{
+
+class ImageIOHandler : public FormatHandler
+{
+public:
+
+	ImageIOHandler();
+	virtual ~ImageIOHandler();
+
+	// Implements FormatHandler.
+
+	virtual bool canDecode(love::filesystem::FileData *data);
+	virtual bool canEncode(ImageData::Format format);
+
+	virtual DecodedImage decode(love::filesystem::FileData *data);
+	virtual EncodedImage encode(const DecodedImage &img, ImageData::Format format);
+
+private:
+
+	love::thread::Mutex *mutex;
+
+}; // ImageIOHandler
+
+} // magpie
+} // image
+} // love
+
+#endif // LOVE_SUPPORT_IMAGEIO
+
+#endif // LOVE_IMAGE_MAGPIE_IMAGEIO_HANDLER_H