diff --git a/platform/macosx/love-framework.xcodeproj/project.pbxproj b/platform/macosx/love-framework.xcodeproj/project.pbxproj index 4c0e0364a..a8564ecf1 100644 --- a/platform/macosx/love-framework.xcodeproj/project.pbxproj +++ b/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 = ""; }; FA03546A1731F3A700284828 /* simplexnoise1234.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simplexnoise1234.cpp; sourceTree = ""; }; FA03546B1731F3A700284828 /* simplexnoise1234.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simplexnoise1234.h; sourceTree = ""; }; + FA0706DF1A93ED2800AF268C /* ImageIOHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageIOHandler.cpp; sourceTree = ""; }; + FA0706E01A93ED2800AF268C /* ImageIOHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageIOHandler.h; sourceTree = ""; }; FA088D841A919E1400F26F29 /* OSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSX.h; sourceTree = ""; }; FA088D851A919E1400F26F29 /* OSX.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSX.mm; sourceTree = ""; }; FA08F5AE16C7525600F007B5 /* Info-Framework.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-Framework.plist"; sourceTree = ""; }; @@ -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 */, diff --git a/src/modules/image/magpie/Image.cpp b/src/modules/image/magpie/Image.cpp index 6c99299f7..fa1d8c4c9 100644 --- a/src/modules/image/magpie/Image.cpp +++ b/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); diff --git a/src/modules/image/magpie/ImageIOHandler.cpp b/src/modules/image/magpie/ImageIOHandler.cpp new file mode 100644 index 000000000..c4a867570 --- /dev/null +++ b/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 + +#if defined(LOVE_IOS) +#include +#elif defined(LOVE_MACOSX) +#include +#endif + +// STL +#include +#include + +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 diff --git a/src/modules/image/magpie/ImageIOHandler.h b/src/modules/image/magpie/ImageIOHandler.h new file mode 100644 index 000000000..d70836b0f --- /dev/null +++ b/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